mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
add new portable service framework
This adds a small service "systemd-portabled" and a matching client "portablectl", which implement the "portable service" concept. The daemon implements the actual operations, is PolicyKit-enabled and is activated on demand with exit-on-idle. Both the daemon and the client are an optional build artifact, enabled by default rhough.
This commit is contained in:
parent
19017acb9f
commit
61d0578b07
24
meson.build
24
meson.build
@ -138,6 +138,7 @@ testsdir = join_paths(prefixdir, 'lib/systemd/tests')
|
|||||||
systemdstatedir = join_paths(localstatedir, 'lib/systemd')
|
systemdstatedir = join_paths(localstatedir, 'lib/systemd')
|
||||||
catalogstatedir = join_paths(systemdstatedir, 'catalog')
|
catalogstatedir = join_paths(systemdstatedir, 'catalog')
|
||||||
randomseeddir = join_paths(localstatedir, 'lib/systemd')
|
randomseeddir = join_paths(localstatedir, 'lib/systemd')
|
||||||
|
profiledir = join_paths(rootlibexecdir, 'portable', 'profile')
|
||||||
|
|
||||||
docdir = get_option('docdir')
|
docdir = get_option('docdir')
|
||||||
if docdir == ''
|
if docdir == ''
|
||||||
@ -1177,6 +1178,7 @@ foreach term : ['utmp',
|
|||||||
'hostnamed',
|
'hostnamed',
|
||||||
'localed',
|
'localed',
|
||||||
'machined',
|
'machined',
|
||||||
|
'portabled',
|
||||||
'networkd',
|
'networkd',
|
||||||
'timedated',
|
'timedated',
|
||||||
'timesyncd',
|
'timesyncd',
|
||||||
@ -1355,6 +1357,7 @@ subdir('src/import')
|
|||||||
subdir('src/kernel-install')
|
subdir('src/kernel-install')
|
||||||
subdir('src/locale')
|
subdir('src/locale')
|
||||||
subdir('src/machine')
|
subdir('src/machine')
|
||||||
|
subdir('src/portable')
|
||||||
subdir('src/nspawn')
|
subdir('src/nspawn')
|
||||||
subdir('src/resolve')
|
subdir('src/resolve')
|
||||||
subdir('src/timedate')
|
subdir('src/timedate')
|
||||||
@ -1716,6 +1719,26 @@ exe = executable('systemctl', 'src/systemctl/systemctl.c',
|
|||||||
install_dir : rootbindir)
|
install_dir : rootbindir)
|
||||||
public_programs += [exe]
|
public_programs += [exe]
|
||||||
|
|
||||||
|
if conf.get('ENABLE_PORTABLED') == 1
|
||||||
|
executable('systemd-portabled',
|
||||||
|
systemd_portabled_sources,
|
||||||
|
include_directories : includes,
|
||||||
|
link_with : [libshared],
|
||||||
|
dependencies : [threads],
|
||||||
|
install_rpath : rootlibexecdir,
|
||||||
|
install : true,
|
||||||
|
install_dir : rootlibexecdir)
|
||||||
|
|
||||||
|
exe = executable('portablectl', 'src/portable/portablectl.c',
|
||||||
|
include_directories : includes,
|
||||||
|
link_with : [libshared],
|
||||||
|
dependencies : [threads],
|
||||||
|
install_rpath : rootlibexecdir,
|
||||||
|
install : true,
|
||||||
|
install_dir : rootlibexecdir)
|
||||||
|
public_programs += [exe]
|
||||||
|
endif
|
||||||
|
|
||||||
foreach alias : ['halt', 'poweroff', 'reboot', 'runlevel', 'shutdown', 'telinit']
|
foreach alias : ['halt', 'poweroff', 'reboot', 'runlevel', 'shutdown', 'telinit']
|
||||||
meson.add_install_script(meson_make_symlink,
|
meson.add_install_script(meson_make_symlink,
|
||||||
join_paths(rootbindir, 'systemctl'),
|
join_paths(rootbindir, 'systemctl'),
|
||||||
@ -2895,6 +2918,7 @@ foreach tuple : [
|
|||||||
['rfkill'],
|
['rfkill'],
|
||||||
['logind'],
|
['logind'],
|
||||||
['machined'],
|
['machined'],
|
||||||
|
['portabled'],
|
||||||
['importd'],
|
['importd'],
|
||||||
['hostnamed'],
|
['hostnamed'],
|
||||||
['timedated'],
|
['timedated'],
|
||||||
|
@ -79,6 +79,8 @@ option('localed', type : 'boolean',
|
|||||||
description : 'install the systemd-localed stack')
|
description : 'install the systemd-localed stack')
|
||||||
option('machined', type : 'boolean',
|
option('machined', type : 'boolean',
|
||||||
description : 'install the systemd-machined stack')
|
description : 'install the systemd-machined stack')
|
||||||
|
option('portabled', type : 'boolean',
|
||||||
|
description : 'install the systemd-portabled stack')
|
||||||
option('networkd', type : 'boolean',
|
option('networkd', type : 'boolean',
|
||||||
description : 'install the systemd-networkd stack')
|
description : 'install the systemd-networkd stack')
|
||||||
option('timedated', type : 'boolean',
|
option('timedated', type : 'boolean',
|
||||||
|
@ -41,6 +41,8 @@
|
|||||||
#define BUS_ERROR_NO_SUCH_USER_MAPPING "org.freedesktop.machine1.NoSuchUserMapping"
|
#define BUS_ERROR_NO_SUCH_USER_MAPPING "org.freedesktop.machine1.NoSuchUserMapping"
|
||||||
#define BUS_ERROR_NO_SUCH_GROUP_MAPPING "org.freedesktop.machine1.NoSuchGroupMapping"
|
#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_NO_SUCH_SESSION "org.freedesktop.login1.NoSuchSession"
|
#define BUS_ERROR_NO_SUCH_SESSION "org.freedesktop.login1.NoSuchSession"
|
||||||
#define BUS_ERROR_NO_SESSION_FOR_PID "org.freedesktop.login1.NoSessionForPID"
|
#define BUS_ERROR_NO_SESSION_FOR_PID "org.freedesktop.login1.NoSessionForPID"
|
||||||
#define BUS_ERROR_NO_SUCH_USER "org.freedesktop.login1.NoSuchUser"
|
#define BUS_ERROR_NO_SUCH_USER "org.freedesktop.login1.NoSuchUser"
|
||||||
|
29
src/portable/meson.build
Normal file
29
src/portable/meson.build
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
|
||||||
|
systemd_portabled_sources = files('''
|
||||||
|
portable.c
|
||||||
|
portable.h
|
||||||
|
portabled-bus.c
|
||||||
|
portabled-image-bus.c
|
||||||
|
portabled-image-bus.h
|
||||||
|
portabled-image.c
|
||||||
|
portabled-image.h
|
||||||
|
portabled-operation.c
|
||||||
|
portabled-operation.h
|
||||||
|
portabled.c
|
||||||
|
portabled.h
|
||||||
|
'''.split())
|
||||||
|
|
||||||
|
if conf.get('ENABLE_PORTABLED') == 1
|
||||||
|
install_data('org.freedesktop.portable1.conf',
|
||||||
|
install_dir : dbuspolicydir)
|
||||||
|
install_data('org.freedesktop.portable1.service',
|
||||||
|
install_dir : dbussystemservicedir)
|
||||||
|
install_data('org.freedesktop.portable1.policy',
|
||||||
|
install_dir : polkitpolicydir)
|
||||||
|
|
||||||
|
install_data('profile/default/service.conf', install_dir : join_paths(profiledir, 'default'))
|
||||||
|
install_data('profile/nonetwork/service.conf', install_dir : join_paths(profiledir, 'nonetwork'))
|
||||||
|
install_data('profile/strict/service.conf', install_dir : join_paths(profiledir, 'strict'))
|
||||||
|
install_data('profile/trusted/service.conf', install_dir : join_paths(profiledir, 'trusted'))
|
||||||
|
endif
|
117
src/portable/org.freedesktop.portable1.conf
Normal file
117
src/portable/org.freedesktop.portable1.conf
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0"?> <!--*-nxml-*-->
|
||||||
|
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||||
|
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||||
|
|
||||||
|
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
|
||||||
|
|
||||||
|
<busconfig>
|
||||||
|
|
||||||
|
<policy user="root">
|
||||||
|
<allow own="org.freedesktop.portable1"/>
|
||||||
|
<allow send_destination="org.freedesktop.portable1"/>
|
||||||
|
<allow receive_sender="org.freedesktop.portable1"/>
|
||||||
|
</policy>
|
||||||
|
|
||||||
|
<policy context="default">
|
||||||
|
<deny send_destination="org.freedesktop.portable1"/>
|
||||||
|
|
||||||
|
<!-- generic interfaces -->
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.DBus.Introspectable"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.DBus.Peer"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.DBus.Properties"
|
||||||
|
send_member="Get"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.DBus.Properties"
|
||||||
|
send_member="GetAll"/>
|
||||||
|
|
||||||
|
<!-- Manager object -->
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Manager"
|
||||||
|
send_member="GetImage"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Manager"
|
||||||
|
send_member="ListImages"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Manager"
|
||||||
|
send_member="GetImageOSRelease"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Manager"
|
||||||
|
send_member="GetImageUnitFiles"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Manager"
|
||||||
|
send_member="GetImageState"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Manager"
|
||||||
|
send_member="AttachImage"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Manager"
|
||||||
|
send_member="DetachImage"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Manager"
|
||||||
|
send_member="RemoveImage"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Manager"
|
||||||
|
send_member="MarkImageReadOnly"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Manager"
|
||||||
|
send_member="SetImageLimit"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Manager"
|
||||||
|
send_member="SetPoolLimit"/>
|
||||||
|
|
||||||
|
<!-- Image object -->
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Image"
|
||||||
|
send_member="GetOSRelease"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Image"
|
||||||
|
send_member="GetUnitFiles"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Image"
|
||||||
|
send_member="GetImageState"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Image"
|
||||||
|
send_member="Attach"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Image"
|
||||||
|
send_member="Detach"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Image"
|
||||||
|
send_member="Remove"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Image"
|
||||||
|
send_member="MarkReadOnly"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.portable1"
|
||||||
|
send_interface="org.freedesktop.portable1.Image"
|
||||||
|
send_member="SetLimit"/>
|
||||||
|
|
||||||
|
<allow receive_sender="org.freedesktop.portable1"/>
|
||||||
|
</policy>
|
||||||
|
|
||||||
|
</busconfig>
|
43
src/portable/org.freedesktop.portable1.policy
Normal file
43
src/portable/org.freedesktop.portable1.policy
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
|
||||||
|
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||||
|
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||||
|
|
||||||
|
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
|
||||||
|
|
||||||
|
<policyconfig>
|
||||||
|
|
||||||
|
<vendor>The systemd Project</vendor>
|
||||||
|
<vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
|
||||||
|
|
||||||
|
<action id="org.freedesktop.portable1.inspect-images">
|
||||||
|
<description gettext-domain="systemd">Inspect a portable service</description>
|
||||||
|
<message gettext-domain="systemd">Authentication is required to inspect a portable service.</message>
|
||||||
|
<defaults>
|
||||||
|
<allow_any>auth_admin</allow_any>
|
||||||
|
<allow_inactive>auth_admin</allow_inactive>
|
||||||
|
<allow_active>auth_admin_keep</allow_active>
|
||||||
|
</defaults>
|
||||||
|
</action>
|
||||||
|
|
||||||
|
<action id="org.freedesktop.portable1.attach-images">
|
||||||
|
<description gettext-domain="systemd">Attach or detach a portable service</description>
|
||||||
|
<message gettext-domain="systemd">Authentication is required to attach or detach a portable service.</message>
|
||||||
|
<defaults>
|
||||||
|
<allow_any>auth_admin</allow_any>
|
||||||
|
<allow_inactive>auth_admin</allow_inactive>
|
||||||
|
<allow_active>auth_admin_keep</allow_active>
|
||||||
|
</defaults>
|
||||||
|
<annotate key="org.freedesktop.policykit.imply">org.freedesktop.systemd1.reload-daemon</annotate>
|
||||||
|
</action>
|
||||||
|
|
||||||
|
<action id="org.freedesktop.portable1.manage-images">
|
||||||
|
<description gettext-domain="systemd">Delete or modify portable service image</description>
|
||||||
|
<message gettext-domain="systemd">Authentication is required to delete or modify a portable service image.</message>
|
||||||
|
<defaults>
|
||||||
|
<allow_any>auth_admin</allow_any>
|
||||||
|
<allow_inactive>auth_admin</allow_inactive>
|
||||||
|
<allow_active>auth_admin_keep</allow_active>
|
||||||
|
</defaults>
|
||||||
|
</action>
|
||||||
|
|
||||||
|
</policyconfig>
|
7
src/portable/org.freedesktop.portable1.service
Normal file
7
src/portable/org.freedesktop.portable1.service
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
|
||||||
|
[D-BUS Service]
|
||||||
|
Name=org.freedesktop.portable1
|
||||||
|
Exec=/bin/false
|
||||||
|
User=root
|
||||||
|
SystemdService=dbus-org.freedesktop.portable1.service
|
1427
src/portable/portable.c
Normal file
1427
src/portable/portable.c
Normal file
File diff suppressed because it is too large
Load Diff
77
src/portable/portable.h
Normal file
77
src/portable/portable.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sd-bus.h"
|
||||||
|
|
||||||
|
#include "hashmap.h"
|
||||||
|
#include "macro.h"
|
||||||
|
#include "set.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
|
||||||
|
typedef struct PortableMetadata {
|
||||||
|
int fd;
|
||||||
|
char *source;
|
||||||
|
char name[];
|
||||||
|
} PortableMetadata;
|
||||||
|
|
||||||
|
#define PORTABLE_METADATA_IS_OS_RELEASE(m) (streq((m)->name, "/etc/os-release"))
|
||||||
|
#define PORTABLE_METADATA_IS_UNIT(m) (!IN_SET((m)->name[0], 0, '/'))
|
||||||
|
|
||||||
|
typedef enum PortableFlags {
|
||||||
|
PORTABLE_PREFER_COPY = 1U << 0,
|
||||||
|
PORTABLE_PREFER_SYMLINK = 1U << 1,
|
||||||
|
PORTABLE_RUNTIME = 1U << 2,
|
||||||
|
} PortableFlags;
|
||||||
|
|
||||||
|
typedef enum PortableChangeType {
|
||||||
|
PORTABLE_COPY,
|
||||||
|
PORTABLE_SYMLINK,
|
||||||
|
PORTABLE_UNLINK,
|
||||||
|
PORTABLE_WRITE,
|
||||||
|
PORTABLE_MKDIR,
|
||||||
|
_PORTABLE_CHANGE_TYPE_MAX,
|
||||||
|
_PORTABLE_CHANGE_TYPE_INVALID = INT_MIN,
|
||||||
|
} PortableChangeType;
|
||||||
|
|
||||||
|
typedef enum PortableState {
|
||||||
|
PORTABLE_DETACHED,
|
||||||
|
PORTABLE_ATTACHED,
|
||||||
|
PORTABLE_ATTACHED_RUNTIME,
|
||||||
|
PORTABLE_ENABLED,
|
||||||
|
PORTABLE_ENABLED_RUNTIME,
|
||||||
|
PORTABLE_RUNNING,
|
||||||
|
PORTABLE_RUNNING_RUNTIME,
|
||||||
|
_PORTABLE_STATE_MAX,
|
||||||
|
_PORTABLE_STATE_INVALID = -1
|
||||||
|
} PortableState;
|
||||||
|
|
||||||
|
typedef struct PortableChange {
|
||||||
|
int type; /* PortableFileChangeType or negative error number */
|
||||||
|
char *path;
|
||||||
|
char *source;
|
||||||
|
} PortableChange;
|
||||||
|
|
||||||
|
PortableMetadata *portable_metadata_unref(PortableMetadata *i);
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(PortableMetadata*, portable_metadata_unref);
|
||||||
|
|
||||||
|
Hashmap *portable_metadata_hashmap_unref(Hashmap *h);
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, portable_metadata_hashmap_unref);
|
||||||
|
|
||||||
|
int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret);
|
||||||
|
|
||||||
|
int portable_extract(const char *image, char **matches, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, sd_bus_error *error);
|
||||||
|
|
||||||
|
int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||||
|
int portable_detach(sd_bus *bus, const char *name_or_path, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||||
|
|
||||||
|
int portable_get_state(sd_bus *bus, const char *name_or_path, PortableFlags flags, PortableState *ret, sd_bus_error *error);
|
||||||
|
|
||||||
|
int portable_get_profiles(char ***ret);
|
||||||
|
|
||||||
|
void portable_changes_free(PortableChange *changes, size_t n_changes);
|
||||||
|
|
||||||
|
const char *portable_change_type_to_string(PortableChangeType t) _const_;
|
||||||
|
PortableChangeType portable_change_type_from_string(const char *t) _pure_;
|
||||||
|
|
||||||
|
const char *portable_state_to_string(PortableState t) _const_;
|
||||||
|
PortableState portable_state_from_string(const char *t) _pure_;
|
965
src/portable/portablectl.c
Normal file
965
src/portable/portablectl.c
Normal file
@ -0,0 +1,965 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include "sd-bus.h"
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "bus-error.h"
|
||||||
|
#include "bus-util.h"
|
||||||
|
#include "def.h"
|
||||||
|
#include "dirent-util.h"
|
||||||
|
#include "fd-util.h"
|
||||||
|
#include "fileio.h"
|
||||||
|
#include "format-table.h"
|
||||||
|
#include "fs-util.h"
|
||||||
|
#include "locale-util.h"
|
||||||
|
#include "machine-image.h"
|
||||||
|
#include "pager.h"
|
||||||
|
#include "parse-util.h"
|
||||||
|
#include "path-util.h"
|
||||||
|
#include "spawn-polkit-agent.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
#include "strv.h"
|
||||||
|
#include "terminal-util.h"
|
||||||
|
#include "verbs.h"
|
||||||
|
|
||||||
|
static bool arg_no_pager = false;
|
||||||
|
static bool arg_legend = true;
|
||||||
|
static bool arg_ask_password = true;
|
||||||
|
static bool arg_quiet = false;
|
||||||
|
static const char *arg_profile = "default";
|
||||||
|
static const char* arg_copy_mode = NULL;
|
||||||
|
static bool arg_runtime = false;
|
||||||
|
static bool arg_reload = true;
|
||||||
|
static bool arg_cat = false;
|
||||||
|
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
|
||||||
|
static char *arg_host = NULL;
|
||||||
|
|
||||||
|
static int determine_image(const char *image, bool permit_non_existing, char **ret) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* If the specified name is a valid image name, we pass it as-is to portabled, which will search for it in the
|
||||||
|
* usual search directories. Otherwise we presume it's a path, and will normalize it on the client's side
|
||||||
|
* (among other things, to make the path independent of the client's working directory) before passing it
|
||||||
|
* over. */
|
||||||
|
|
||||||
|
if (image_name_is_valid(image)) {
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
if (!arg_quiet && laccess(image, F_OK) >= 0)
|
||||||
|
log_warning("Ambiguous invocation: current working directory contains file matching non-path argument '%s', ignoring. "
|
||||||
|
"Prefix argument with './' to force reference to file in current working directory.", image);
|
||||||
|
|
||||||
|
c = strdup(image);
|
||||||
|
if (!c)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
*ret = c;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg_transport != BUS_TRANSPORT_LOCAL) {
|
||||||
|
log_error("Operations on images by path not supported when connecting to remote systems.");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = chase_symlinks(image, NULL, CHASE_TRAIL_SLASH | (permit_non_existing ? CHASE_NONEXISTENT : 0), ret);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Cannot normalize specified image path '%s': %m", image);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int extract_prefix(const char *path, char **ret) {
|
||||||
|
_cleanup_free_ char *name = NULL;
|
||||||
|
const char *bn, *underscore;
|
||||||
|
size_t m;
|
||||||
|
|
||||||
|
bn = basename(path);
|
||||||
|
|
||||||
|
underscore = strchr(bn, '_');
|
||||||
|
if (underscore)
|
||||||
|
m = underscore - bn;
|
||||||
|
else {
|
||||||
|
const char *e;
|
||||||
|
|
||||||
|
e = endswith(bn, ".raw");
|
||||||
|
if (!e)
|
||||||
|
e = strchr(bn, 0);
|
||||||
|
|
||||||
|
m = e - bn;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = strndup(bn, m);
|
||||||
|
if (!name)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* A slightly reduced version of what's permitted in unit names. With ':' and '\' are removed, as well as '_'
|
||||||
|
* which we use as delimiter for the second part of the image string, which we ignore for now. */
|
||||||
|
if (!in_charset(name, DIGITS LETTERS "-."))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!filename_is_valid(name))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
*ret = name;
|
||||||
|
name = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int determine_matches(const char *image, char **l, bool allow_any, char ***ret) {
|
||||||
|
char **k;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* Determine the matches to apply. If the list is empty we derive the match from the image name. If the list
|
||||||
|
* contains exactly the "-" we return a wildcard list (which is the empty list), but only if this is expressly
|
||||||
|
* permitted. */
|
||||||
|
|
||||||
|
if (strv_isempty(l)) {
|
||||||
|
char *prefix;
|
||||||
|
|
||||||
|
r = extract_prefix(image, &prefix);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to extract prefix of image name '%s': %m", image);
|
||||||
|
|
||||||
|
if (!arg_quiet)
|
||||||
|
log_info("(Matching unit files with prefix '%s'.)", prefix);
|
||||||
|
|
||||||
|
k = NULL;
|
||||||
|
r = strv_consume(&k, prefix);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
} else if (strv_equal(l, STRV_MAKE("-"))) {
|
||||||
|
|
||||||
|
if (!allow_any) {
|
||||||
|
log_error("Refusing all unit file match.");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arg_quiet)
|
||||||
|
log_info("(Matching all unit files.)");
|
||||||
|
k = NULL;
|
||||||
|
} else {
|
||||||
|
_cleanup_free_ char *joined = NULL;
|
||||||
|
|
||||||
|
k = strv_copy(l);
|
||||||
|
if (!k)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
joined = strv_join(k, "', '");
|
||||||
|
if (!joined)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
if (!arg_quiet)
|
||||||
|
log_info("(Matching unit files with prefixes '%s'.)", joined);
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret = k;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acquire_bus(sd_bus **bus) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
|
||||||
|
if (*bus)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
r = bus_connect_transport(arg_transport, arg_host, false, bus);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to connect to bus: %m");
|
||||||
|
|
||||||
|
(void) sd_bus_set_allow_interactive_authorization(*bus, arg_ask_password);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int maybe_reload(sd_bus **bus) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (!arg_reload)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
r = acquire_bus(bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_new_method_call(
|
||||||
|
*bus,
|
||||||
|
&m,
|
||||||
|
"org.freedesktop.systemd1",
|
||||||
|
"/org/freedesktop/systemd1",
|
||||||
|
"org.freedesktop.systemd1.Manager",
|
||||||
|
"Reload");
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
/* Reloading the daemon may take long, hence set a longer timeout here */
|
||||||
|
r = sd_bus_call(*bus, m, DEFAULT_TIMEOUT_USEC * 2, &error, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int inspect_image(int argc, char *argv[], void *userdata) {
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
_cleanup_strv_free_ char **matches = NULL;
|
||||||
|
_cleanup_free_ char *image = NULL;
|
||||||
|
bool nl = false, header = false;
|
||||||
|
const void *data;
|
||||||
|
const char *path;
|
||||||
|
size_t sz;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = determine_image(argv[1], false, &image);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = determine_matches(argv[1], argv + 2, true, &matches);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_new_method_call(
|
||||||
|
bus,
|
||||||
|
&m,
|
||||||
|
"org.freedesktop.portable1",
|
||||||
|
"/org/freedesktop/portable1",
|
||||||
|
"org.freedesktop.portable1.Manager",
|
||||||
|
"GetImageMetadata");
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_message_append(m, "s", image);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_message_append_strv(m, matches);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_call(bus, m, 0, &error, &reply);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to inspect image metadata: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
|
r = sd_bus_message_read(reply, "s", &path);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_message_read_array(reply, 'y', &data, &sz);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
(void) pager_open(arg_no_pager, false);
|
||||||
|
|
||||||
|
if (arg_cat) {
|
||||||
|
printf("%s-- OS Release: --%s\n", ansi_highlight(), ansi_normal());
|
||||||
|
fwrite(data, sz, 1, stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
nl = true;
|
||||||
|
} else {
|
||||||
|
const char *pretty_portable = NULL, *pretty_os = NULL;
|
||||||
|
|
||||||
|
_cleanup_fclose_ FILE *f;
|
||||||
|
|
||||||
|
f = fmemopen((void*) data, sz, "re");
|
||||||
|
if (!f)
|
||||||
|
return log_error_errno(errno, "Failed to open /etc/os-release buffer: %m");
|
||||||
|
|
||||||
|
r = parse_env_file(f, "/etc/os-release", NEWLINE,
|
||||||
|
"PORTABLE_PRETTY_NAME", &pretty_portable,
|
||||||
|
"PRETTY_NAME", &pretty_os,
|
||||||
|
NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse /etc/os-release: %m");
|
||||||
|
|
||||||
|
printf("Image:\n\t%s\n"
|
||||||
|
"Portable Service:\n\t%s\n"
|
||||||
|
"Operating System:\n\t%s\n",
|
||||||
|
path,
|
||||||
|
strna(pretty_portable),
|
||||||
|
strna(pretty_os));
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_enter_container(reply, 'a', "{say}");
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
r = sd_bus_message_enter_container(reply, 'e', "say");
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
if (r == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
r = sd_bus_message_read(reply, "s", &name);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_message_read_array(reply, 'y', &data, &sz);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
if (arg_cat) {
|
||||||
|
if (nl)
|
||||||
|
fputc('\n', stdout);
|
||||||
|
|
||||||
|
printf("%s-- Unit file: %s --%s\n", ansi_highlight(), name, ansi_normal());
|
||||||
|
fwrite(data, sz, 1, stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
nl = true;
|
||||||
|
} else {
|
||||||
|
if (!header) {
|
||||||
|
fputs("Unit files:\n", stdout);
|
||||||
|
header = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fputc('\t', stdout);
|
||||||
|
fputs(name, stdout);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_exit_container(reply);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_exit_container(reply);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_changes(sd_bus_message *m) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (arg_quiet)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
r = sd_bus_message_enter_container(m, 'a', "(sss)");
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const char *type, *path, *source;
|
||||||
|
|
||||||
|
r = sd_bus_message_read(m, "(sss)", &type, &path, &source);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
if (r == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (streq(type, "symlink"))
|
||||||
|
log_info("Created symlink %s %s %s.", path, special_glyph(ARROW), source);
|
||||||
|
else if (streq(type, "copy")) {
|
||||||
|
if (isempty(source))
|
||||||
|
log_info("Copied %s.", path);
|
||||||
|
else
|
||||||
|
log_info("Copied %s %s %s.", source, special_glyph(ARROW), path);
|
||||||
|
} else if (streq(type, "unlink"))
|
||||||
|
log_info("Removed %s.", path);
|
||||||
|
else if (streq(type, "write"))
|
||||||
|
log_info("Written %s.", path);
|
||||||
|
else if (streq(type, "mkdir"))
|
||||||
|
log_info("Created directory %s.", path);
|
||||||
|
else
|
||||||
|
log_error("Unexpected change: %s/%s/%s", type, path, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_exit_container(m);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int attach_image(int argc, char *argv[], void *userdata) {
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
_cleanup_strv_free_ char **matches = NULL;
|
||||||
|
_cleanup_free_ char *image = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = determine_image(argv[1], false, &image);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = determine_matches(argv[1], argv + 2, false, &matches);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||||
|
|
||||||
|
r = sd_bus_message_new_method_call(
|
||||||
|
bus,
|
||||||
|
&m,
|
||||||
|
"org.freedesktop.portable1",
|
||||||
|
"/org/freedesktop/portable1",
|
||||||
|
"org.freedesktop.portable1.Manager",
|
||||||
|
"AttachImage");
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_message_append(m, "s", image);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_message_append_strv(m, matches);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_message_append(m, "sbs", arg_profile, arg_runtime, arg_copy_mode);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_call(bus, m, 0, &error, &reply);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to attach image: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
|
(void) maybe_reload(&bus);
|
||||||
|
|
||||||
|
print_changes(reply);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int detach_image(int argc, char *argv[], void *userdata) {
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
_cleanup_free_ char *image = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = determine_image(argv[1], true, &image);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||||
|
|
||||||
|
r = sd_bus_call_method(
|
||||||
|
bus,
|
||||||
|
"org.freedesktop.portable1",
|
||||||
|
"/org/freedesktop/portable1",
|
||||||
|
"org.freedesktop.portable1.Manager",
|
||||||
|
"DetachImage",
|
||||||
|
&error,
|
||||||
|
&reply,
|
||||||
|
"sb", image, arg_runtime);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to detach image: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
|
(void) maybe_reload(&bus);
|
||||||
|
|
||||||
|
print_changes(reply);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int list_images(int argc, char *argv[], void *userdata) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_call_method(
|
||||||
|
bus,
|
||||||
|
"org.freedesktop.portable1",
|
||||||
|
"/org/freedesktop/portable1",
|
||||||
|
"org.freedesktop.portable1.Manager",
|
||||||
|
"ListImages",
|
||||||
|
&error,
|
||||||
|
&reply,
|
||||||
|
NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to list images: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
|
table = table_new("NAME", "TYPE", "RO", "CRTIME", "MTIME", "USAGE", "STATE");
|
||||||
|
if (!table)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = sd_bus_message_enter_container(reply, 'a', "(ssbtttso)");
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const char *name, *type, *state, *object;
|
||||||
|
uint64_t crtime, mtime, usage;
|
||||||
|
TableCell *cell;
|
||||||
|
bool ro_bool;
|
||||||
|
int ro_int;
|
||||||
|
|
||||||
|
r = sd_bus_message_read(reply, "(ssbtttso)", &name, &type, &ro_int, &crtime, &mtime, &usage, &state, &object);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
if (r == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
r = table_add_many(table,
|
||||||
|
TABLE_STRING, name,
|
||||||
|
TABLE_STRING, type);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to add row to table: %m");
|
||||||
|
|
||||||
|
ro_bool = ro_int;
|
||||||
|
r = table_add_cell(table, &cell, TABLE_BOOLEAN, &ro_bool);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to add row to table: %m");
|
||||||
|
|
||||||
|
if (ro_bool) {
|
||||||
|
r = table_set_color(table, cell, ansi_highlight_red());
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to set table cell color: %m");
|
||||||
|
}
|
||||||
|
|
||||||
|
r = table_add_many(table,
|
||||||
|
TABLE_TIMESTAMP, crtime,
|
||||||
|
TABLE_TIMESTAMP, mtime,
|
||||||
|
TABLE_SIZE, usage);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to add row to table: %m");
|
||||||
|
|
||||||
|
r = table_add_cell(table, &cell, TABLE_STRING, state);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to add row to table: %m");
|
||||||
|
|
||||||
|
if (!streq(state, "detached")) {
|
||||||
|
r = table_set_color(table, cell, ansi_highlight_green());
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to set table cell color: %m");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_exit_container(reply);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
if (table_get_rows(table) > 1) {
|
||||||
|
r = table_set_sort(table, (size_t) 0, (size_t) -1);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to sort table: %m");
|
||||||
|
|
||||||
|
table_set_header(table, arg_legend);
|
||||||
|
|
||||||
|
r = table_print(table, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to show table: %m");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg_legend) {
|
||||||
|
if (table_get_rows(table) > 1)
|
||||||
|
printf("\n%zu images listed.\n", table_get_rows(table) - 1);
|
||||||
|
else
|
||||||
|
printf("No images.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int remove_image(int argc, char *argv[], void *userdata) {
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
int r, i;
|
||||||
|
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||||
|
|
||||||
|
r = sd_bus_message_new_method_call(
|
||||||
|
bus,
|
||||||
|
&m,
|
||||||
|
"org.freedesktop.portable1",
|
||||||
|
"/org/freedesktop/portable1",
|
||||||
|
"org.freedesktop.portable1.Manager",
|
||||||
|
"RemoveImage");
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_message_append(m, "s", argv[i]);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
/* This is a slow operation, hence turn off any method call timeouts */
|
||||||
|
r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_only_image(int argc, char *argv[], void *userdata) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
int b = true, r;
|
||||||
|
|
||||||
|
if (argc > 2) {
|
||||||
|
b = parse_boolean(argv[2]);
|
||||||
|
if (b < 0)
|
||||||
|
return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||||
|
|
||||||
|
r = sd_bus_call_method(
|
||||||
|
bus,
|
||||||
|
"org.freedesktop.portable1",
|
||||||
|
"/org/freedesktop/portable1",
|
||||||
|
"org.freedesktop.portable1.Manager",
|
||||||
|
"MarkImageReadOnly",
|
||||||
|
&error,
|
||||||
|
NULL,
|
||||||
|
"sb", argv[1], b);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_limit(int argc, char *argv[], void *userdata) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
uint64_t limit;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||||
|
|
||||||
|
if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
|
||||||
|
limit = (uint64_t) -1;
|
||||||
|
else {
|
||||||
|
r = parse_size(argv[argc-1], 1024, &limit);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse size: %s", argv[argc-1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 2)
|
||||||
|
/* With two arguments changes the quota limit of the specified image */
|
||||||
|
r = sd_bus_call_method(
|
||||||
|
bus,
|
||||||
|
"org.freedesktop.portable1",
|
||||||
|
"/org/freedesktop/portable1",
|
||||||
|
"org.freedesktop.portable1.Manager",
|
||||||
|
"SetImageLimit",
|
||||||
|
&error,
|
||||||
|
NULL,
|
||||||
|
"st", argv[1], limit);
|
||||||
|
else
|
||||||
|
/* With one argument changes the pool quota limit */
|
||||||
|
r = sd_bus_call_method(
|
||||||
|
bus,
|
||||||
|
"org.freedesktop.portable1",
|
||||||
|
"/org/freedesktop/portable1",
|
||||||
|
"org.freedesktop.portable1.Manager",
|
||||||
|
"SetPoolLimit",
|
||||||
|
&error,
|
||||||
|
NULL,
|
||||||
|
"t", limit);
|
||||||
|
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_image_attached(int argc, char *argv[], void *userdata) {
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
_cleanup_free_ char *image = NULL;
|
||||||
|
const char *state;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = determine_image(argv[1], true, &image);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_call_method(
|
||||||
|
bus,
|
||||||
|
"org.freedesktop.portable1",
|
||||||
|
"/org/freedesktop/portable1",
|
||||||
|
"org.freedesktop.portable1.Manager",
|
||||||
|
"GetImageState",
|
||||||
|
&error,
|
||||||
|
&reply,
|
||||||
|
"s", image);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get image state: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
|
r = sd_bus_message_read(reply, "s", &state);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!arg_quiet)
|
||||||
|
puts(state);
|
||||||
|
|
||||||
|
return streq(state, "detached");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dump_profiles(void) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
|
_cleanup_(closedirp) DIR *d = NULL;
|
||||||
|
char **i;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_get_property_strv(
|
||||||
|
bus,
|
||||||
|
"org.freedesktop.portable1",
|
||||||
|
"/org/freedesktop/portable1",
|
||||||
|
"org.freedesktop.portable1.Manager",
|
||||||
|
"Profiles",
|
||||||
|
&error,
|
||||||
|
&l);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to acquire list of profiles: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
|
if (arg_legend)
|
||||||
|
log_info("Available unit profiles:");
|
||||||
|
|
||||||
|
STRV_FOREACH(i, l) {
|
||||||
|
fputs(*i, stdout);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int help(int argc, char *argv[], void *userdata) {
|
||||||
|
|
||||||
|
(void) pager_open(arg_no_pager, false);
|
||||||
|
|
||||||
|
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
|
||||||
|
"Attach or detach portable services from the local system.\n\n"
|
||||||
|
" -h --help Show this help\n"
|
||||||
|
" --version Show package version\n"
|
||||||
|
" --no-pager Do not pipe output into a pager\n"
|
||||||
|
" --no-legend Do not show the headers and footers\n"
|
||||||
|
" --no-ask-password Do not ask for system passwords\n"
|
||||||
|
" -H --host=[USER@]HOST Operate on remote host\n"
|
||||||
|
" -M --machine=CONTAINER Operate on local container\n"
|
||||||
|
" -q --quiet Suppress informational messages\n"
|
||||||
|
" -p --profile=PROFILE Pick security profile for portable service\n"
|
||||||
|
" --copy=copy|auto|symlink Prefer copying or symlinks if possible\n"
|
||||||
|
" --runtime Attach portable service until next reboot only\n"
|
||||||
|
" --no-reload Don't reload the system and service manager\n"
|
||||||
|
" --cat When inspecting include unit and os-release file\n"
|
||||||
|
" contents\n\n"
|
||||||
|
"Commands:\n"
|
||||||
|
" list List available portable service images\n"
|
||||||
|
" attach NAME|PATH [PREFIX...]\n"
|
||||||
|
" Attach the specified portable service image\n"
|
||||||
|
" detach NAME|PATH Detach the specified portable service image\n"
|
||||||
|
" inspect NAME|PATH [PREFIX...]\n"
|
||||||
|
" Show details of specified portable service image\n"
|
||||||
|
" is-attached NAME|PATH Query if portable service image is attached\n"
|
||||||
|
" read-only NAME|PATH [BOOL] Mark or unmark portable service image read-only\n"
|
||||||
|
" remove NAME|PATH... Remove a portable service image\n"
|
||||||
|
" set-limit [NAME|PATH] Set image or pool size limit (disk quota)\n"
|
||||||
|
, program_invocation_short_name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_argv(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ARG_VERSION = 0x100,
|
||||||
|
ARG_NO_PAGER,
|
||||||
|
ARG_NO_LEGEND,
|
||||||
|
ARG_NO_ASK_PASSWORD,
|
||||||
|
ARG_COPY,
|
||||||
|
ARG_RUNTIME,
|
||||||
|
ARG_NO_RELOAD,
|
||||||
|
ARG_CAT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct option options[] = {
|
||||||
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ "version", no_argument, NULL, ARG_VERSION },
|
||||||
|
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||||
|
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||||
|
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
|
||||||
|
{ "host", required_argument, NULL, 'H' },
|
||||||
|
{ "machine", required_argument, NULL, 'M' },
|
||||||
|
{ "quiet", no_argument, NULL, 'q' },
|
||||||
|
{ "profile", required_argument, NULL, 'p' },
|
||||||
|
{ "copy", required_argument, NULL, ARG_COPY },
|
||||||
|
{ "runtime", no_argument, NULL, ARG_RUNTIME },
|
||||||
|
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
|
||||||
|
{ "cat", no_argument, NULL, ARG_CAT },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(argc >= 0);
|
||||||
|
assert(argv);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
int c;
|
||||||
|
|
||||||
|
c = getopt_long(argc, argv, "hH:M:qp:", options, NULL);
|
||||||
|
if (c < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
help(0, NULL, NULL);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case ARG_VERSION:
|
||||||
|
return version();
|
||||||
|
|
||||||
|
case ARG_NO_PAGER:
|
||||||
|
arg_no_pager = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_NO_LEGEND:
|
||||||
|
arg_legend = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_NO_ASK_PASSWORD:
|
||||||
|
arg_ask_password = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'H':
|
||||||
|
arg_transport = BUS_TRANSPORT_REMOTE;
|
||||||
|
arg_host = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'M':
|
||||||
|
arg_transport = BUS_TRANSPORT_MACHINE;
|
||||||
|
arg_host = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'q':
|
||||||
|
arg_quiet = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
if (!filename_is_valid(optarg)) {
|
||||||
|
log_error("Unit profile name not valid: %s", optarg);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streq(optarg, "help"))
|
||||||
|
return dump_profiles();
|
||||||
|
|
||||||
|
arg_profile = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_COPY:
|
||||||
|
if (streq(optarg, "auto"))
|
||||||
|
arg_copy_mode = NULL;
|
||||||
|
else if (STR_IN_SET(optarg, "copy", "symlink"))
|
||||||
|
arg_copy_mode = optarg;
|
||||||
|
else {
|
||||||
|
log_error("Failed to parse --copy= argument: %s", optarg);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_RUNTIME:
|
||||||
|
arg_runtime = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_NO_RELOAD:
|
||||||
|
arg_reload = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_CAT:
|
||||||
|
arg_cat = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert_not_reached("Unhandled option");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
static const Verb verbs[] = {
|
||||||
|
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
||||||
|
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_images },
|
||||||
|
{ "attach", 2, VERB_ANY, 0, attach_image },
|
||||||
|
{ "detach", 2, 2, 0, detach_image },
|
||||||
|
{ "inspect", 2, VERB_ANY, 0, inspect_image },
|
||||||
|
{ "is-attached", 2, 2, 0, is_image_attached },
|
||||||
|
{ "read-only", 2, 3, 0, read_only_image },
|
||||||
|
{ "remove", 2, VERB_ANY, 0, remove_image },
|
||||||
|
{ "set-limit", 3, 3, 0, set_limit },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
int r;
|
||||||
|
|
||||||
|
log_parse_environment();
|
||||||
|
log_open();
|
||||||
|
|
||||||
|
r = parse_argv(argc, argv);
|
||||||
|
if (r <= 0)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
r = dispatch_verb(argc, argv, verbs, NULL);
|
||||||
|
|
||||||
|
finish:
|
||||||
|
pager_close();
|
||||||
|
|
||||||
|
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
}
|
402
src/portable/portabled-bus.c
Normal file
402
src/portable/portabled-bus.c
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "btrfs-util.h"
|
||||||
|
#include "bus-common-errors.h"
|
||||||
|
#include "bus-util.h"
|
||||||
|
#include "fd-util.h"
|
||||||
|
#include "io-util.h"
|
||||||
|
#include "machine-image.h"
|
||||||
|
#include "portable.h"
|
||||||
|
#include "portabled-bus.h"
|
||||||
|
#include "portabled-image-bus.h"
|
||||||
|
#include "portabled-image.h"
|
||||||
|
#include "portabled.h"
|
||||||
|
#include "strv.h"
|
||||||
|
#include "user-util.h"
|
||||||
|
|
||||||
|
static int property_get_pool_path(
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
assert(reply);
|
||||||
|
|
||||||
|
return sd_bus_message_append(reply, "s", "/var/lib/portables");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int property_get_pool_usage(
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
_cleanup_close_ int fd = -1;
|
||||||
|
uint64_t usage = (uint64_t) -1;
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
assert(reply);
|
||||||
|
|
||||||
|
fd = open("/var/lib/portables", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
|
||||||
|
if (fd >= 0) {
|
||||||
|
BtrfsQuotaInfo q;
|
||||||
|
|
||||||
|
if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0)
|
||||||
|
usage = q.referenced;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd_bus_message_append(reply, "t", usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int property_get_pool_limit(
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
_cleanup_close_ int fd = -1;
|
||||||
|
uint64_t size = (uint64_t) -1;
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
assert(reply);
|
||||||
|
|
||||||
|
fd = open("/var/lib/portables", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
|
||||||
|
if (fd >= 0) {
|
||||||
|
BtrfsQuotaInfo q;
|
||||||
|
|
||||||
|
if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0)
|
||||||
|
size = q.referenced_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd_bus_message_append(reply, "t", size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int property_get_profiles(
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
assert(reply);
|
||||||
|
|
||||||
|
r = portable_get_profiles(&l);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_message_append_strv(reply, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_get_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
_cleanup_free_ char *p = NULL;
|
||||||
|
Manager *m = userdata;
|
||||||
|
const char *name;
|
||||||
|
Image *image;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
r = sd_bus_message_read(message, "s", &name);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = bus_image_acquire(m, message, name, NULL, BUS_IMAGE_REFUSE_BY_PATH, NULL, &image, error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = bus_image_path(image, &p);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_reply_method_return(message, "o", p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
|
_cleanup_(image_hashmap_freep) Hashmap *images = NULL;
|
||||||
|
Manager *m = userdata;
|
||||||
|
Image *image;
|
||||||
|
Iterator i;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
images = hashmap_new(&string_hash_ops);
|
||||||
|
if (!images)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = manager_image_cache_discover(m, images, error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_new_method_return(message, &reply);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_open_container(reply, 'a', "(ssbtttso)");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
HASHMAP_FOREACH(image, images, i) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error_state = SD_BUS_ERROR_NULL;
|
||||||
|
PortableState state = _PORTABLE_STATE_INVALID;
|
||||||
|
_cleanup_free_ char *p = NULL;
|
||||||
|
|
||||||
|
r = bus_image_path(image, &p);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = portable_get_state(
|
||||||
|
sd_bus_message_get_bus(message),
|
||||||
|
image->path,
|
||||||
|
0,
|
||||||
|
&state,
|
||||||
|
&error_state);
|
||||||
|
if (r < 0)
|
||||||
|
log_debug_errno(r, "Failed to get state of image '%s', ignoring: %s",
|
||||||
|
image->path, bus_error_message(&error_state, r));
|
||||||
|
|
||||||
|
r = sd_bus_message_append(reply, "(ssbtttso)",
|
||||||
|
image->name,
|
||||||
|
image_type_to_string(image->type),
|
||||||
|
image->read_only,
|
||||||
|
image->crtime,
|
||||||
|
image->mtime,
|
||||||
|
image->usage,
|
||||||
|
portable_state_to_string(state),
|
||||||
|
p);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_close_container(reply);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_send(NULL, reply, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int redirect_method_to_image(
|
||||||
|
Manager *m,
|
||||||
|
sd_bus_message *message,
|
||||||
|
sd_bus_error *error,
|
||||||
|
int (*method)(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error* error)) {
|
||||||
|
|
||||||
|
const char *name_or_path;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
assert(message);
|
||||||
|
assert(method);
|
||||||
|
|
||||||
|
r = sd_bus_message_read(message, "s", &name_or_path);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return method(m, message, name_or_path, NULL, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_get_image_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return redirect_method_to_image(userdata, message, error, bus_image_common_get_os_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_get_image_metadata(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return redirect_method_to_image(userdata, message, error, bus_image_common_get_metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
const char *name_or_path;
|
||||||
|
PortableState state;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
|
||||||
|
r = sd_bus_message_read(message, "s", &name_or_path);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = portable_get_state(
|
||||||
|
sd_bus_message_get_bus(message),
|
||||||
|
name_or_path,
|
||||||
|
0,
|
||||||
|
&state,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_reply_method_return(message, "s", portable_state_to_string(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_attach_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return redirect_method_to_image(userdata, message, error, bus_image_common_attach);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
PortableChange *changes = NULL;
|
||||||
|
Manager *m = userdata;
|
||||||
|
size_t n_changes = 0;
|
||||||
|
const char *name_or_path;
|
||||||
|
int r, runtime;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
/* Note that we do not redirect detaching to the image object here, because we want to allow that users can
|
||||||
|
* detach already deleted images too, in case the user already deleted an image before properly detaching
|
||||||
|
* it. */
|
||||||
|
|
||||||
|
r = sd_bus_message_read(message, "sb", &name_or_path, &runtime);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = bus_verify_polkit_async(
|
||||||
|
message,
|
||||||
|
CAP_SYS_ADMIN,
|
||||||
|
"org.freedesktop.portable1.attach-images",
|
||||||
|
NULL,
|
||||||
|
false,
|
||||||
|
UID_INVALID,
|
||||||
|
&m->polkit_registry,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return 1; /* Will call us back */
|
||||||
|
|
||||||
|
r = portable_detach(
|
||||||
|
sd_bus_message_get_bus(message),
|
||||||
|
name_or_path,
|
||||||
|
runtime ? PORTABLE_RUNTIME : 0,
|
||||||
|
&changes,
|
||||||
|
&n_changes,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
r = reply_portable_changes(message, changes, n_changes);
|
||||||
|
|
||||||
|
finish:
|
||||||
|
portable_changes_free(changes, n_changes);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return redirect_method_to_image(userdata, message, error, bus_image_common_remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_mark_image_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return redirect_method_to_image(userdata, message, error, bus_image_common_mark_read_only);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return redirect_method_to_image(userdata, message, error, bus_image_common_set_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
Manager *m = userdata;
|
||||||
|
uint64_t limit;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
|
||||||
|
r = sd_bus_message_read(message, "t", &limit);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
|
||||||
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
|
||||||
|
|
||||||
|
r = bus_verify_polkit_async(
|
||||||
|
message,
|
||||||
|
CAP_SYS_ADMIN,
|
||||||
|
"org.freedesktop.portable1.manage-images",
|
||||||
|
NULL,
|
||||||
|
false,
|
||||||
|
UID_INVALID,
|
||||||
|
&m->polkit_registry,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return 1; /* Will call us back */
|
||||||
|
|
||||||
|
(void) btrfs_qgroup_set_limit("/var/lib/portables", 0, limit);
|
||||||
|
|
||||||
|
r = btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit);
|
||||||
|
if (r == -ENOTTY)
|
||||||
|
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
|
||||||
|
if (r < 0)
|
||||||
|
return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m");
|
||||||
|
|
||||||
|
return sd_bus_reply_method_return(message, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sd_bus_vtable manager_vtable[] = {
|
||||||
|
SD_BUS_VTABLE_START(0),
|
||||||
|
SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0),
|
||||||
|
SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0),
|
||||||
|
SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0),
|
||||||
|
SD_BUS_PROPERTY("Profiles", "as", property_get_profiles, 0, 0),
|
||||||
|
SD_BUS_METHOD("GetImage", "s", "o", method_get_image, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("ListImages", NULL, "a(ssbtttso)", method_list_images, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("GetImageOSRelease", "s", "a{ss}", method_get_image_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("GetImageState", "s", "s", method_get_image_state, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("GetImageMetadata", "sas", "saya{say}", method_get_image_metadata, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("AttachImage", "sassbs", "a(sss)", method_attach_image, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("DetachImage", "sb", "a(sss)", method_detach_image, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_VTABLE_END
|
||||||
|
};
|
||||||
|
|
||||||
|
int reply_portable_changes(sd_bus_message *m, const PortableChange *changes, size_t n_changes) {
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
|
size_t i;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
assert(changes || n_changes == 0);
|
||||||
|
|
||||||
|
r = sd_bus_message_new_method_return(m, &reply);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_open_container(reply, 'a', "(sss)");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
for (i = 0; i < n_changes; i++) {
|
||||||
|
r = sd_bus_message_append(reply, "(sss)",
|
||||||
|
portable_change_type_to_string(changes[i].type),
|
||||||
|
changes[i].path,
|
||||||
|
changes[i].source);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_close_container(reply);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_send(NULL, reply, NULL);
|
||||||
|
}
|
10
src/portable/portabled-bus.h
Normal file
10
src/portable/portabled-bus.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sd-bus.h"
|
||||||
|
|
||||||
|
#include "portable.h"
|
||||||
|
|
||||||
|
extern const sd_bus_vtable manager_vtable[];
|
||||||
|
|
||||||
|
int reply_portable_changes(sd_bus_message *m, const PortableChange *changes, size_t n_changes);
|
733
src/portable/portabled-image-bus.c
Normal file
733
src/portable/portabled-image-bus.c
Normal file
@ -0,0 +1,733 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "bus-common-errors.h"
|
||||||
|
#include "bus-label.h"
|
||||||
|
#include "bus-util.h"
|
||||||
|
#include "fd-util.h"
|
||||||
|
#include "fileio.h"
|
||||||
|
#include "io-util.h"
|
||||||
|
#include "machine-image.h"
|
||||||
|
#include "portable.h"
|
||||||
|
#include "portabled-bus.h"
|
||||||
|
#include "portabled-image-bus.h"
|
||||||
|
#include "portabled-image.h"
|
||||||
|
#include "portabled.h"
|
||||||
|
#include "process-util.h"
|
||||||
|
#include "strv.h"
|
||||||
|
#include "user-util.h"
|
||||||
|
|
||||||
|
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
|
||||||
|
|
||||||
|
int bus_image_common_get_os_release(
|
||||||
|
Manager *m,
|
||||||
|
sd_bus_message *message,
|
||||||
|
const char *name_or_path,
|
||||||
|
Image *image,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(name_or_path || image);
|
||||||
|
assert(message);
|
||||||
|
|
||||||
|
if (!m) {
|
||||||
|
assert(image);
|
||||||
|
m = image->userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = bus_image_acquire(m,
|
||||||
|
message,
|
||||||
|
name_or_path,
|
||||||
|
image,
|
||||||
|
BUS_IMAGE_AUTHENTICATE_BY_PATH,
|
||||||
|
"org.freedesktop.portable1.inspect-images",
|
||||||
|
&image,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0) /* Will call us back */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!image->metadata_valid) {
|
||||||
|
r = image_read_metadata(image);
|
||||||
|
if (r < 0)
|
||||||
|
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||||
|
}
|
||||||
|
|
||||||
|
return bus_reply_pair_array(message, image->os_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bus_image_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return bus_image_common_get_os_release(NULL, message, NULL, userdata, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_fd(sd_bus_message *m, PortableMetadata *d) {
|
||||||
|
_cleanup_fclose_ FILE *f = NULL;
|
||||||
|
_cleanup_free_ char *buf = NULL;
|
||||||
|
size_t n;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
assert(d);
|
||||||
|
assert(d->fd >= 0);
|
||||||
|
|
||||||
|
f = fdopen(d->fd, "re");
|
||||||
|
if (!f)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
d->fd = -1;
|
||||||
|
|
||||||
|
r = read_full_stream(f, &buf, &n);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_message_append_array(m, 'y', buf, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bus_image_common_get_metadata(
|
||||||
|
Manager *m,
|
||||||
|
sd_bus_message *message,
|
||||||
|
const char *name_or_path,
|
||||||
|
Image *image,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
|
||||||
|
_cleanup_(portable_metadata_hashmap_unrefp) Hashmap *unit_files = NULL;
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
|
_cleanup_free_ PortableMetadata **sorted = NULL;
|
||||||
|
_cleanup_strv_free_ char **matches = NULL;
|
||||||
|
size_t i;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(name_or_path || image);
|
||||||
|
assert(message);
|
||||||
|
|
||||||
|
if (!m) {
|
||||||
|
assert(image);
|
||||||
|
m = image->userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_read_strv(message, &matches);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = bus_image_acquire(m,
|
||||||
|
message,
|
||||||
|
name_or_path,
|
||||||
|
image,
|
||||||
|
BUS_IMAGE_AUTHENTICATE_BY_PATH,
|
||||||
|
"org.freedesktop.portable1.inspect-images",
|
||||||
|
&image,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0) /* Will call us back */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
r = portable_extract(
|
||||||
|
image->path,
|
||||||
|
matches,
|
||||||
|
&os_release,
|
||||||
|
&unit_files,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = portable_metadata_hashmap_to_sorted_array(unit_files, &sorted);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_new_method_return(message, &reply);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_append(reply, "s", image->path);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = append_fd(reply, os_release);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_open_container(reply, 'a', "{say}");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
for (i = 0; i < hashmap_size(unit_files); i++) {
|
||||||
|
|
||||||
|
|
||||||
|
r = sd_bus_message_open_container(reply, 'e', "say");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_append(reply, "s", sorted[i]->name);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = append_fd(reply, sorted[i]);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_close_container(reply);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_close_container(reply);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_send(NULL, reply, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bus_image_method_get_metadata(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return bus_image_common_get_metadata(NULL, message, NULL, userdata, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bus_image_method_get_state(
|
||||||
|
sd_bus_message *message,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
Image *image = userdata;
|
||||||
|
PortableState state;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
assert(image);
|
||||||
|
|
||||||
|
r = portable_get_state(
|
||||||
|
sd_bus_message_get_bus(message),
|
||||||
|
image->path,
|
||||||
|
0,
|
||||||
|
&state,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_reply_method_return(message, "s", portable_state_to_string(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
int bus_image_common_attach(
|
||||||
|
Manager *m,
|
||||||
|
sd_bus_message *message,
|
||||||
|
const char *name_or_path,
|
||||||
|
Image *image,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
_cleanup_free_ char **matches = NULL;
|
||||||
|
PortableChange *changes = NULL;
|
||||||
|
PortableFlags flags = 0;
|
||||||
|
const char *copy_mode;
|
||||||
|
size_t n_changes = 0;
|
||||||
|
const char *profile;
|
||||||
|
int runtime, r;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
assert(name_or_path || image);
|
||||||
|
|
||||||
|
if (!m) {
|
||||||
|
assert(image);
|
||||||
|
m = image->userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_read_strv(message, &matches);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_read(message, "sbs", &profile, &runtime, ©_mode);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (streq(copy_mode, "symlink"))
|
||||||
|
flags |= PORTABLE_PREFER_SYMLINK;
|
||||||
|
else if (streq(copy_mode, "copy"))
|
||||||
|
flags |= PORTABLE_PREFER_COPY;
|
||||||
|
else if (!isempty(copy_mode))
|
||||||
|
return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
|
||||||
|
|
||||||
|
if (runtime)
|
||||||
|
flags |= PORTABLE_RUNTIME;
|
||||||
|
|
||||||
|
r = bus_image_acquire(m,
|
||||||
|
message,
|
||||||
|
name_or_path,
|
||||||
|
image,
|
||||||
|
BUS_IMAGE_AUTHENTICATE_ALL,
|
||||||
|
"org.freedesktop.portable1.attach-images",
|
||||||
|
&image,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0) /* Will call us back */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
r = portable_attach(
|
||||||
|
sd_bus_message_get_bus(message),
|
||||||
|
image->path,
|
||||||
|
matches,
|
||||||
|
profile,
|
||||||
|
flags,
|
||||||
|
&changes,
|
||||||
|
&n_changes,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
r = reply_portable_changes(message, changes, n_changes);
|
||||||
|
|
||||||
|
finish:
|
||||||
|
portable_changes_free(changes, n_changes);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bus_image_method_attach(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return bus_image_common_attach(NULL, message, NULL, userdata, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bus_image_method_detach(
|
||||||
|
sd_bus_message *message,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
PortableChange *changes = NULL;
|
||||||
|
Image *image = userdata;
|
||||||
|
Manager *m = image->userdata;
|
||||||
|
size_t n_changes = 0;
|
||||||
|
int r, runtime;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
assert(image);
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
r = sd_bus_message_read(message, "b", &runtime);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = bus_verify_polkit_async(
|
||||||
|
message,
|
||||||
|
CAP_SYS_ADMIN,
|
||||||
|
"org.freedesktop.portable1.attach-images",
|
||||||
|
NULL,
|
||||||
|
false,
|
||||||
|
UID_INVALID,
|
||||||
|
&m->polkit_registry,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return 1; /* Will call us back */
|
||||||
|
|
||||||
|
r = portable_detach(
|
||||||
|
sd_bus_message_get_bus(message),
|
||||||
|
image->path,
|
||||||
|
runtime ? PORTABLE_RUNTIME : 0,
|
||||||
|
&changes,
|
||||||
|
&n_changes,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
r = reply_portable_changes(message, changes, n_changes);
|
||||||
|
|
||||||
|
finish:
|
||||||
|
portable_changes_free(changes, n_changes);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bus_image_common_remove(
|
||||||
|
Manager *m,
|
||||||
|
sd_bus_message *message,
|
||||||
|
const char *name_or_path,
|
||||||
|
Image *image,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
_cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
|
||||||
|
_cleanup_(sigkill_waitp) pid_t child = 0;
|
||||||
|
PortableState state;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
assert(name_or_path || image);
|
||||||
|
|
||||||
|
if (!m) {
|
||||||
|
assert(image);
|
||||||
|
m = image->userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m->n_operations >= OPERATIONS_MAX)
|
||||||
|
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
|
||||||
|
|
||||||
|
r = bus_image_acquire(m,
|
||||||
|
message,
|
||||||
|
name_or_path,
|
||||||
|
image,
|
||||||
|
BUS_IMAGE_AUTHENTICATE_ALL,
|
||||||
|
"org.freedesktop.portable1.manage-images",
|
||||||
|
&image,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return 1; /* Will call us back */
|
||||||
|
|
||||||
|
r = portable_get_state(
|
||||||
|
sd_bus_message_get_bus(message),
|
||||||
|
image->path,
|
||||||
|
0,
|
||||||
|
&state,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (state != PORTABLE_DETACHED)
|
||||||
|
return sd_bus_error_set_errnof(error, EBUSY, "Image '%s' is not detached, refusing.", image->path);
|
||||||
|
|
||||||
|
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
|
||||||
|
return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
|
||||||
|
|
||||||
|
r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
|
||||||
|
if (r < 0)
|
||||||
|
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
|
||||||
|
if (r == 0) {
|
||||||
|
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
|
||||||
|
|
||||||
|
r = image_remove(image);
|
||||||
|
if (r < 0) {
|
||||||
|
(void) write(errno_pipe_fd[1], &r, sizeof(r));
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
_exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
|
||||||
|
|
||||||
|
r = operation_new(m, child, message, errno_pipe_fd[0], NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
child = 0;
|
||||||
|
errno_pipe_fd[0] = -1;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return bus_image_common_remove(NULL, message, NULL, userdata, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bus_image_common_mark_read_only(
|
||||||
|
Manager *m,
|
||||||
|
sd_bus_message *message,
|
||||||
|
const char *name_or_path,
|
||||||
|
Image *image,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
int r, read_only;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
assert(name_or_path || image);
|
||||||
|
|
||||||
|
if (!m) {
|
||||||
|
assert(image);
|
||||||
|
m = image->userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_read(message, "b", &read_only);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = bus_image_acquire(m,
|
||||||
|
message,
|
||||||
|
name_or_path,
|
||||||
|
image,
|
||||||
|
BUS_IMAGE_AUTHENTICATE_ALL,
|
||||||
|
"org.freedesktop.portable1.manage-images",
|
||||||
|
&image,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return 1; /* Will call us back */
|
||||||
|
|
||||||
|
r = image_read_only(image, read_only);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_reply_method_return(message, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return bus_image_common_mark_read_only(NULL, message, NULL, userdata, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bus_image_common_set_limit(
|
||||||
|
Manager *m,
|
||||||
|
sd_bus_message *message,
|
||||||
|
const char *name_or_path,
|
||||||
|
Image *image,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
uint64_t limit;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
assert(name_or_path || image);
|
||||||
|
|
||||||
|
if (!m) {
|
||||||
|
assert(image);
|
||||||
|
m = image->userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_read(message, "t", &limit);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
|
||||||
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
|
||||||
|
|
||||||
|
r = bus_image_acquire(m,
|
||||||
|
message,
|
||||||
|
name_or_path,
|
||||||
|
image,
|
||||||
|
BUS_IMAGE_AUTHENTICATE_ALL,
|
||||||
|
"org.freedesktop.portable1.manage-images",
|
||||||
|
&image,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return 1; /* Will call us back */
|
||||||
|
|
||||||
|
r = image_set_limit(image, limit);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_reply_method_return(message, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return bus_image_common_set_limit(NULL, message, NULL, userdata, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sd_bus_vtable image_vtable[] = {
|
||||||
|
SD_BUS_VTABLE_START(0),
|
||||||
|
SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
|
||||||
|
SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
|
||||||
|
SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
|
||||||
|
SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
|
||||||
|
SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
|
||||||
|
SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
|
||||||
|
SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
|
||||||
|
SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
|
||||||
|
SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
|
||||||
|
SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
|
||||||
|
SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("GetMedatadata", "as", "saya{say}", bus_image_method_get_metadata, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("GetState", NULL, "s", bus_image_method_get_state, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("Attach", "assbs", "a(sss)", bus_image_method_attach, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("Detach", "b", "a(sss)", bus_image_method_detach, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_VTABLE_END
|
||||||
|
};
|
||||||
|
|
||||||
|
int bus_image_path(Image *image, char **ret) {
|
||||||
|
assert(image);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
if (!image->discoverable)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return sd_bus_path_encode("/org/freedesktop/portable1/image", image->name, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bus_image_acquire(
|
||||||
|
Manager *m,
|
||||||
|
sd_bus_message *message,
|
||||||
|
const char *name_or_path,
|
||||||
|
Image *image,
|
||||||
|
ImageAcquireMode mode,
|
||||||
|
const char *polkit_action,
|
||||||
|
Image **ret,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
_cleanup_(image_unrefp) Image *loaded = NULL;
|
||||||
|
Image *cached;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
assert(message);
|
||||||
|
assert(name_or_path || image);
|
||||||
|
assert(mode >= 0);
|
||||||
|
assert(mode < _BUS_IMAGE_ACQUIRE_MODE_MAX);
|
||||||
|
assert(polkit_action || mode == BUS_IMAGE_REFUSE_BY_PATH);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
/* Acquires an 'Image' object if not acquired yet, and enforces necessary authentication while doing so. */
|
||||||
|
|
||||||
|
if (mode == BUS_IMAGE_AUTHENTICATE_ALL) {
|
||||||
|
r = bus_verify_polkit_async(
|
||||||
|
message,
|
||||||
|
CAP_SYS_ADMIN,
|
||||||
|
polkit_action,
|
||||||
|
NULL,
|
||||||
|
false,
|
||||||
|
UID_INVALID,
|
||||||
|
&m->polkit_registry,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0) { /* Will call us back */
|
||||||
|
*ret = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Already passed in? */
|
||||||
|
if (image) {
|
||||||
|
*ret = image;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Let's see if this image is already cached? */
|
||||||
|
cached = manager_image_cache_get(m, name_or_path);
|
||||||
|
if (cached) {
|
||||||
|
*ret = cached;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image_name_is_valid(name_or_path)) {
|
||||||
|
|
||||||
|
/* If it's a short name, let's search for it */
|
||||||
|
r = image_find(IMAGE_PORTABLE, name_or_path, &loaded);
|
||||||
|
if (r == -ENOENT)
|
||||||
|
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE, "No image '%s' found.", name_or_path);
|
||||||
|
|
||||||
|
/* other errors are handled below… */
|
||||||
|
} else {
|
||||||
|
/* Don't accept path if this is always forbidden */
|
||||||
|
if (mode == BUS_IMAGE_REFUSE_BY_PATH)
|
||||||
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Expected image name, not path in place of '%s'.", name_or_path);
|
||||||
|
|
||||||
|
if (!path_is_absolute(name_or_path))
|
||||||
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is not valid or not a valid path.", name_or_path);
|
||||||
|
|
||||||
|
if (!path_is_normalized(name_or_path))
|
||||||
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image path '%s' is not normalized.", name_or_path);
|
||||||
|
|
||||||
|
if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) {
|
||||||
|
r = bus_verify_polkit_async(
|
||||||
|
message,
|
||||||
|
CAP_SYS_ADMIN,
|
||||||
|
polkit_action,
|
||||||
|
NULL,
|
||||||
|
false,
|
||||||
|
UID_INVALID,
|
||||||
|
&m->polkit_registry,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0) { /* Will call us back */
|
||||||
|
*ret = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = image_from_path(name_or_path, &loaded);
|
||||||
|
}
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* Add what we just loaded to the cache. This has as side-effect that the object stays in memory until the
|
||||||
|
* cache is purged again, i.e. at least for the current event loop iteration, which is all we need, and which
|
||||||
|
* means we don't actually need to ref the return object. */
|
||||||
|
r = manager_image_cache_add(m, loaded);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
*ret = loaded;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bus_image_object_find(
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
void *userdata,
|
||||||
|
void **found,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
_cleanup_free_ char *e = NULL;
|
||||||
|
Manager *m = userdata;
|
||||||
|
Image *image = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
assert(path);
|
||||||
|
assert(interface);
|
||||||
|
assert(found);
|
||||||
|
|
||||||
|
r = sd_bus_path_decode(path, "/org/freedesktop/portable1/image", &e);
|
||||||
|
if (r < 0)
|
||||||
|
return 0;
|
||||||
|
if (r == 0)
|
||||||
|
goto not_found;
|
||||||
|
|
||||||
|
r = bus_image_acquire(m, sd_bus_get_current_message(bus), e, NULL, BUS_IMAGE_REFUSE_BY_PATH, NULL, &image, error);
|
||||||
|
if (r == -ENOENT)
|
||||||
|
goto not_found;
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
*found = image;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
not_found:
|
||||||
|
*found = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bus_image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
|
||||||
|
_cleanup_(image_hashmap_freep) Hashmap *images = NULL;
|
||||||
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
|
size_t n_allocated = 0, n = 0;
|
||||||
|
Manager *m = userdata;
|
||||||
|
Image *image;
|
||||||
|
Iterator i;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
assert(path);
|
||||||
|
assert(nodes);
|
||||||
|
|
||||||
|
images = hashmap_new(&string_hash_ops);
|
||||||
|
if (!images)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = manager_image_cache_discover(m, images, error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
HASHMAP_FOREACH(image, images, i) {
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
r = bus_image_path(image, &p);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!GREEDY_REALLOC(l, n_allocated, n+2)) {
|
||||||
|
free(p);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
l[n++] = p;
|
||||||
|
l[n] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*nodes = TAKE_PTR(l);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
40
src/portable/portabled-image-bus.h
Normal file
40
src/portable/portabled-image-bus.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
|
#include "sd-bus.h"
|
||||||
|
|
||||||
|
#include "machine-image.h"
|
||||||
|
#include "portabled.h"
|
||||||
|
|
||||||
|
int bus_image_common_get_os_release(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
|
||||||
|
int bus_image_common_get_metadata(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
|
||||||
|
int bus_image_common_attach(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
|
||||||
|
int bus_image_common_remove(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
|
||||||
|
int bus_image_common_mark_read_only(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
|
||||||
|
int bus_image_common_set_limit(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error);
|
||||||
|
|
||||||
|
extern const sd_bus_vtable image_vtable[];
|
||||||
|
|
||||||
|
int bus_image_path(Image *image, char **ret);
|
||||||
|
|
||||||
|
/* So here's some complexity: some of operations can either take an image name, or a fully qualified file system path
|
||||||
|
* to an image. We need to authenticate differently when processing these two: images referenced via simple image names
|
||||||
|
* mean the images are located in the image search path and thus safe for limited read access for unprivileged
|
||||||
|
* clients. For operations on images located anywhere else we need explicit authentication however, so that
|
||||||
|
* unprivileged clients can't make us open arbitrary files in the file system.
|
||||||
|
*
|
||||||
|
* The "Image" bus objects directly represent images in the image search path, but do not exist for path-referenced
|
||||||
|
* images. Hence, when requesting a bus object we need to refuse references by file system path, but still allow
|
||||||
|
* references by image name. Depending on the operation to execute potentially we need to authenticate in all cases. */
|
||||||
|
|
||||||
|
typedef enum ImageAcquireMode {
|
||||||
|
BUS_IMAGE_REFUSE_BY_PATH, /* allow by name + prohibit by path */
|
||||||
|
BUS_IMAGE_AUTHENTICATE_BY_PATH, /* allow by name + polkit by path */
|
||||||
|
BUS_IMAGE_AUTHENTICATE_ALL, /* polkit by name + polkit by path */
|
||||||
|
_BUS_IMAGE_ACQUIRE_MODE_MAX,
|
||||||
|
_BUS_IMAGE_ACQUIRE_MODE_INVALID = -1
|
||||||
|
} ImageAcquireMode;
|
||||||
|
|
||||||
|
int bus_image_acquire(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, ImageAcquireMode mode, const char *polkit_action, Image **ret, sd_bus_error *error);
|
||||||
|
|
||||||
|
int bus_image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
|
||||||
|
int bus_image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
|
105
src/portable/portabled-image.c
Normal file
105
src/portable/portabled-image.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
|
#include "portable.h"
|
||||||
|
#include "portabled-image.h"
|
||||||
|
#include "portabled.h"
|
||||||
|
|
||||||
|
Image *manager_image_cache_get(Manager *m, const char *name_or_path) {
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
return hashmap_get(m->image_cache, name_or_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int image_cache_flush(sd_event_source *s, void *userdata) {
|
||||||
|
Manager *m = userdata;
|
||||||
|
|
||||||
|
assert(s);
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
hashmap_clear_with_destructor(m->image_cache, image_unref);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int manager_image_cache_initialize(Manager *m) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
r = hashmap_ensure_allocated(&m->image_cache, &string_hash_ops);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* We flush the cache as soon as we are idle again */
|
||||||
|
if (!m->image_cache_defer_event) {
|
||||||
|
r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_cache_flush, m);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int manager_image_cache_add(Manager *m, Image *image) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
/* We add the specified image to the cache under two keys.
|
||||||
|
*
|
||||||
|
* 1. Always under its path
|
||||||
|
*
|
||||||
|
* 2. If the image was discovered in the search path (i.e. its discoverable boolean set) we'll also add it
|
||||||
|
* under its short name.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
r = manager_image_cache_initialize(m);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
image->userdata = m;
|
||||||
|
|
||||||
|
r = hashmap_put(m->image_cache, image->path, image);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
image_ref(image);
|
||||||
|
|
||||||
|
if (image->discoverable) {
|
||||||
|
r = hashmap_put(m->image_cache, image->name, image);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
image_ref(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int manager_image_cache_discover(Manager *m, Hashmap *images, sd_bus_error *error) {
|
||||||
|
Image *image;
|
||||||
|
Iterator i;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
/* A wrapper around image_discover() (for finding images in search path) and portable_discover_attached() (for
|
||||||
|
* finding attached images). */
|
||||||
|
|
||||||
|
r = image_discover(IMAGE_PORTABLE, images);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
HASHMAP_FOREACH(image, images, i)
|
||||||
|
(void) manager_image_cache_add(m, image);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
11
src/portable/portabled-image.h
Normal file
11
src/portable/portabled-image.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
|
#include "hashmap.h"
|
||||||
|
#include "machine-image.h"
|
||||||
|
#include "portabled.h"
|
||||||
|
|
||||||
|
Image *manager_image_cache_get(Manager *m, const char *name_or_path);
|
||||||
|
|
||||||
|
int manager_image_cache_add(Manager *m, Image *image);
|
||||||
|
|
||||||
|
int manager_image_cache_discover(Manager *m, Hashmap *images, sd_bus_error *error);
|
128
src/portable/portabled-operation.c
Normal file
128
src/portable/portabled-operation.c
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "fd-util.h"
|
||||||
|
#include "portabled-operation.h"
|
||||||
|
#include "process-util.h"
|
||||||
|
|
||||||
|
static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
Operation *o = userdata;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(o);
|
||||||
|
assert(si);
|
||||||
|
|
||||||
|
log_debug("Operating " PID_FMT " is now complete with code=%s status=%i",
|
||||||
|
o->pid,
|
||||||
|
sigchld_code_to_string(si->si_code), si->si_status);
|
||||||
|
|
||||||
|
o->pid = 0;
|
||||||
|
|
||||||
|
if (si->si_code != CLD_EXITED) {
|
||||||
|
r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (si->si_status == EXIT_SUCCESS)
|
||||||
|
r = 0;
|
||||||
|
else if (read(o->errno_fd, &r, sizeof(r)) != sizeof(r)) { /* Try to acquire error code for failed operation */
|
||||||
|
r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed.");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o->done) {
|
||||||
|
/* A completion routine is set for this operation, call it. */
|
||||||
|
r = o->done(o, r, &error);
|
||||||
|
if (r < 0) {
|
||||||
|
if (!sd_bus_error_is_set(&error))
|
||||||
|
sd_bus_error_set_errno(&error, r);
|
||||||
|
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* The default operation when done is to simply return an error on failure or an empty success
|
||||||
|
* message on success. */
|
||||||
|
if (r < 0) {
|
||||||
|
sd_bus_error_set_errno(&error, r);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_reply_method_return(o->message, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
log_error_errno(r, "Failed to reply to message: %m");
|
||||||
|
}
|
||||||
|
|
||||||
|
operation_free(o);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
r = sd_bus_reply_method_error(o->message, &error);
|
||||||
|
if (r < 0)
|
||||||
|
log_error_errno(r, "Failed to reply to message: %m");
|
||||||
|
|
||||||
|
operation_free(o);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int operation_new(Manager *manager, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret) {
|
||||||
|
Operation *o;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(manager);
|
||||||
|
assert(child > 1);
|
||||||
|
assert(message);
|
||||||
|
assert(errno_fd >= 0);
|
||||||
|
|
||||||
|
o = new0(Operation, 1);
|
||||||
|
if (!o)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
o->extra_fd = -1;
|
||||||
|
|
||||||
|
r = sd_event_add_child(manager->event, &o->event_source, child, WEXITED, operation_done, o);
|
||||||
|
if (r < 0) {
|
||||||
|
free(o);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
o->pid = child;
|
||||||
|
o->message = sd_bus_message_ref(message);
|
||||||
|
o->errno_fd = errno_fd;
|
||||||
|
|
||||||
|
LIST_PREPEND(operations, manager->operations, o);
|
||||||
|
manager->n_operations++;
|
||||||
|
o->manager = manager;
|
||||||
|
|
||||||
|
log_debug("Started new operation " PID_FMT ".", child);
|
||||||
|
|
||||||
|
/* At this point we took ownership of both the child and the errno file descriptor! */
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
*ret = o;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Operation *operation_free(Operation *o) {
|
||||||
|
if (!o)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sd_event_source_unref(o->event_source);
|
||||||
|
|
||||||
|
safe_close(o->errno_fd);
|
||||||
|
safe_close(o->extra_fd);
|
||||||
|
|
||||||
|
if (o->pid > 1)
|
||||||
|
(void) sigkill_wait(o->pid);
|
||||||
|
|
||||||
|
sd_bus_message_unref(o->message);
|
||||||
|
|
||||||
|
if (o->manager) {
|
||||||
|
LIST_REMOVE(operations, o->manager->operations, o);
|
||||||
|
o->manager->n_operations--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mfree(o);
|
||||||
|
}
|
29
src/portable/portabled-operation.h
Normal file
29
src/portable/portabled-operation.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "sd-bus.h"
|
||||||
|
#include "sd-event.h"
|
||||||
|
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
typedef struct Operation Operation;
|
||||||
|
|
||||||
|
#include "portabled.h"
|
||||||
|
|
||||||
|
#define OPERATIONS_MAX 64
|
||||||
|
|
||||||
|
struct Operation {
|
||||||
|
Manager *manager;
|
||||||
|
pid_t pid;
|
||||||
|
sd_bus_message *message;
|
||||||
|
int errno_fd;
|
||||||
|
int extra_fd;
|
||||||
|
sd_event_source *event_source;
|
||||||
|
int (*done)(Operation *o, int ret, sd_bus_error *error);
|
||||||
|
LIST_FIELDS(Operation, operations);
|
||||||
|
};
|
||||||
|
|
||||||
|
int operation_new(Manager *manager, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret);
|
||||||
|
Operation *operation_free(Operation *o);
|
168
src/portable/portabled.c
Normal file
168
src/portable/portabled.c
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
|
#include "sd-bus.h"
|
||||||
|
#include "sd-daemon.h"
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "bus-util.h"
|
||||||
|
#include "def.h"
|
||||||
|
#include "portabled-bus.h"
|
||||||
|
#include "portabled-image-bus.h"
|
||||||
|
#include "portabled.h"
|
||||||
|
#include "process-util.h"
|
||||||
|
#include "signal-util.h"
|
||||||
|
|
||||||
|
static Manager* manager_unref(Manager *m);
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
|
||||||
|
|
||||||
|
static int manager_new(Manager **ret) {
|
||||||
|
_cleanup_(manager_unrefp) Manager *m = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
m = new0(Manager, 1);
|
||||||
|
if (!m)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = sd_event_default(&m->event);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
(void) sd_event_set_watchdog(m->event, true);
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(m);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Manager* manager_unref(Manager *m) {
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
hashmap_free_with_destructor(m->image_cache, image_unref);
|
||||||
|
|
||||||
|
sd_event_source_unref(m->image_cache_defer_event);
|
||||||
|
|
||||||
|
bus_verify_polkit_async_registry_free(m->polkit_registry);
|
||||||
|
|
||||||
|
sd_bus_unref(m->bus);
|
||||||
|
sd_event_unref(m->event);
|
||||||
|
|
||||||
|
return mfree(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int manager_connect_bus(Manager *m) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
assert(!m->bus);
|
||||||
|
|
||||||
|
r = sd_bus_default_system(&m->bus);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to connect to system bus: %m");
|
||||||
|
|
||||||
|
r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/portable1", "org.freedesktop.portable1.Manager", manager_vtable, m);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to add manager object vtable: %m");
|
||||||
|
|
||||||
|
r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/portable1/image", "org.freedesktop.portable1.Image", image_vtable, bus_image_object_find, m);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to add image object vtable: %m");
|
||||||
|
|
||||||
|
r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/portable1/image", bus_image_node_enumerator, m);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to add image enumerator: %m");
|
||||||
|
|
||||||
|
r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.portable1", 0, NULL, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to request name: %m");
|
||||||
|
|
||||||
|
r = sd_bus_attach_event(m->bus, m->event, 0);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to attach bus to event loop: %m");
|
||||||
|
|
||||||
|
(void) sd_bus_set_exit_on_disconnect(m->bus, true);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int manager_startup(Manager *m) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
r = manager_connect_bus(m);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_idle(void *userdata) {
|
||||||
|
Manager *m = userdata;
|
||||||
|
|
||||||
|
return !m->operations;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int manager_run(Manager *m) {
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
return bus_event_loop_with_idle(
|
||||||
|
m->event,
|
||||||
|
m->bus,
|
||||||
|
"org.freedesktop.portable1",
|
||||||
|
DEFAULT_EXIT_USEC,
|
||||||
|
check_idle, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
_cleanup_(manager_unrefp) Manager *m = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
log_set_target(LOG_TARGET_AUTO);
|
||||||
|
log_parse_environment();
|
||||||
|
log_open();
|
||||||
|
|
||||||
|
umask(0022);
|
||||||
|
|
||||||
|
if (argc != 1) {
|
||||||
|
log_error("This program takes no arguments.");
|
||||||
|
r = -EINVAL;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, -1) >= 0);
|
||||||
|
|
||||||
|
r = manager_new(&m);
|
||||||
|
if (r < 0) {
|
||||||
|
log_error_errno(r, "Failed to allocate manager object: %m");
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = manager_startup(m);
|
||||||
|
if (r < 0) {
|
||||||
|
log_error_errno(r, "Failed to fully start up daemon: %m");
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("systemd-portabled running as pid " PID_FMT, getpid_cached());
|
||||||
|
|
||||||
|
sd_notify(false,
|
||||||
|
"READY=1\n"
|
||||||
|
"STATUS=Processing requests...");
|
||||||
|
|
||||||
|
r = manager_run(m);
|
||||||
|
|
||||||
|
log_debug("systemd-portabled stopped as pid " PID_FMT, getpid_cached());
|
||||||
|
|
||||||
|
finish:
|
||||||
|
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
|
||||||
|
}
|
25
src/portable/portabled.h
Normal file
25
src/portable/portabled.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sd-bus.h"
|
||||||
|
#include "sd-event.h"
|
||||||
|
|
||||||
|
#include "hashmap.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
typedef struct Manager Manager;
|
||||||
|
|
||||||
|
#include "portabled-operation.h"
|
||||||
|
|
||||||
|
struct Manager {
|
||||||
|
sd_event *event;
|
||||||
|
sd_bus *bus;
|
||||||
|
|
||||||
|
Hashmap *polkit_registry;
|
||||||
|
|
||||||
|
Hashmap *image_cache;
|
||||||
|
sd_event_source *image_cache_defer_event;
|
||||||
|
|
||||||
|
LIST_HEAD(Operation, operations);
|
||||||
|
unsigned n_operations;
|
||||||
|
};
|
30
src/portable/profile/default/service.conf
Normal file
30
src/portable/profile/default/service.conf
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# The "default" security profile for services, i.e. a number of useful restrictions
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
MountAPIVFS=yes
|
||||||
|
TemporaryFileSystem=/run
|
||||||
|
BindReadOnlyPaths=/run/systemd/notify
|
||||||
|
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
|
||||||
|
BindReadOnlyPaths=/etc/machine-id
|
||||||
|
BindReadOnlyPaths=/etc/resolv.conf
|
||||||
|
BindReadOnlyPaths=/run/dbus/system_bus_socket
|
||||||
|
DynamicUser=yes
|
||||||
|
RemoveIPC=yes
|
||||||
|
CapabilityBoundingSet=CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER \
|
||||||
|
CAP_FSETID CAP_IPC_LOCK CAP_IPC_OWNER CAP_KILL CAP_MKNOD CAP_NET_ADMIN \
|
||||||
|
CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_SETGID CAP_SETPCAP \
|
||||||
|
CAP_SETUID CAP_SYS_ADMIN CAP_SYS_CHROOT CAP_SYS_NICE CAP_SYS_RESOURCE
|
||||||
|
PrivateTmp=yes
|
||||||
|
PrivateDevices=yes
|
||||||
|
PrivateUsers=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
|
||||||
|
LockPersonality=yes
|
||||||
|
MemoryDenyWriteExecute=yes
|
||||||
|
RestrictRealtime=yes
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
SystemCallArchitectures=native
|
30
src/portable/profile/nonetwork/service.conf
Normal file
30
src/portable/profile/nonetwork/service.conf
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# The "nonetwork" security profile for services, i.e. like "default" but without networking
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
MountAPIVFS=yes
|
||||||
|
TemporaryFileSystem=/run
|
||||||
|
BindReadOnlyPaths=/run/systemd/notify
|
||||||
|
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
|
||||||
|
BindReadOnlyPaths=/etc/machine-id
|
||||||
|
BindReadOnlyPaths=/run/dbus/system_bus_socket
|
||||||
|
DynamicUser=yes
|
||||||
|
RemoveIPC=yes
|
||||||
|
CapabilityBoundingSet=CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER \
|
||||||
|
CAP_FSETID CAP_IPC_LOCK CAP_IPC_OWNER CAP_KILL CAP_MKNOD CAP_SETGID CAP_SETPCAP \
|
||||||
|
CAP_SETUID CAP_SYS_ADMIN CAP_SYS_CHROOT CAP_SYS_NICE CAP_SYS_RESOURCE
|
||||||
|
PrivateTmp=yes
|
||||||
|
PrivateDevices=yes
|
||||||
|
PrivateUsers=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
RestrictAddressFamilies=AF_UNIX AF_NETLINK
|
||||||
|
LockPersonality=yes
|
||||||
|
MemoryDenyWriteExecute=yes
|
||||||
|
RestrictRealtime=yes
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
PrivateNetwork=yes
|
||||||
|
IPAddressDeny=any
|
29
src/portable/profile/strict/service.conf
Normal file
29
src/portable/profile/strict/service.conf
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# The "strict" security profile for services, all options turned on
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
MountAPIVFS=yes
|
||||||
|
TemporaryFileSystem=/run
|
||||||
|
BindReadOnlyPaths=/run/systemd/notify
|
||||||
|
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
|
||||||
|
BindReadOnlyPaths=/etc/machine-id
|
||||||
|
DynamicUser=yes
|
||||||
|
RemoveIPC=yes
|
||||||
|
CapabilityBoundingSet=
|
||||||
|
PrivateTmp=yes
|
||||||
|
PrivateDevices=yes
|
||||||
|
PrivateUsers=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
RestrictAddressFamilies=AF_UNIX
|
||||||
|
LockPersonality=yes
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
MemoryDenyWriteExecute=yes
|
||||||
|
RestrictRealtime=yes
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
PrivateNetwork=yes
|
||||||
|
IPAddressDeny=any
|
||||||
|
TasksMax=4
|
7
src/portable/profile/trusted/service.conf
Normal file
7
src/portable/profile/trusted/service.conf
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# The "trusted" profile for services, i.e. no restrictions are applied
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
MountAPIVFS=yes
|
||||||
|
BindPaths=/run
|
||||||
|
BindReadOnlyPaths=/etc/machine-id
|
||||||
|
BindReadOnlyPaths=/etc/resolv.conf
|
@ -8,6 +8,7 @@ tmpfiles = [['home.conf', ''],
|
|||||||
['journal-nocow.conf', ''],
|
['journal-nocow.conf', ''],
|
||||||
['systemd-nologin.conf', ''],
|
['systemd-nologin.conf', ''],
|
||||||
['systemd-nspawn.conf', 'ENABLE_MACHINED'],
|
['systemd-nspawn.conf', 'ENABLE_MACHINED'],
|
||||||
|
['portables.conf', 'ENABLE_PORTABLED'],
|
||||||
['tmp.conf', ''],
|
['tmp.conf', ''],
|
||||||
['x11.conf', ''],
|
['x11.conf', ''],
|
||||||
['legacy.conf', 'HAVE_SYSV_COMPAT'],
|
['legacy.conf', 'HAVE_SYSV_COMPAT'],
|
||||||
|
4
tmpfiles.d/portables.conf
Normal file
4
tmpfiles.d/portables.conf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
# See tmpfiles.d(5) for details
|
||||||
|
|
||||||
|
Q /var/lib/portables 0700
|
@ -177,6 +177,8 @@ in_units = [
|
|||||||
['systemd-networkd-wait-online.service', 'ENABLE_NETWORKD',
|
['systemd-networkd-wait-online.service', 'ENABLE_NETWORKD',
|
||||||
join_paths(pkgsysconfdir, 'system/network-online.target.wants/')],
|
join_paths(pkgsysconfdir, 'system/network-online.target.wants/')],
|
||||||
['systemd-nspawn@.service', ''],
|
['systemd-nspawn@.service', ''],
|
||||||
|
['systemd-portabled.service', 'ENABLE_PORTABLED',
|
||||||
|
'dbus-org.freedesktop.portable1.service'],
|
||||||
['systemd-poweroff.service', ''],
|
['systemd-poweroff.service', ''],
|
||||||
['systemd-quotacheck.service', 'ENABLE_QUOTACHECK'],
|
['systemd-quotacheck.service', 'ENABLE_QUOTACHECK'],
|
||||||
['systemd-random-seed.service', 'ENABLE_RANDOMSEED',
|
['systemd-random-seed.service', 'ENABLE_RANDOMSEED',
|
||||||
|
26
units/systemd-portabled.service.in
Normal file
26
units/systemd-portabled.service.in
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
#
|
||||||
|
# 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=Portable Service Manager
|
||||||
|
Documentation=man:systemd-portabled.service(8)
|
||||||
|
RequiresMountsFor=/var/lib/portables
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=@rootlibexecdir@/systemd-portabled
|
||||||
|
BusName=org.freedesktop.portable1
|
||||||
|
WatchdogSec=3min
|
||||||
|
CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD
|
||||||
|
MemoryDenyWriteExecute=yes
|
||||||
|
RestrictRealtime=yes
|
||||||
|
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
|
||||||
|
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io @reboot @swap
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
LockPersonality=yes
|
||||||
|
IPAddressDeny=any
|
Loading…
Reference in New Issue
Block a user