Merge pull request #29325 from poettering/varlink-introspect
varlink: add introspection support + varlinkctl + varlinkify one first command line tool (systemd-pcrextend)
This commit is contained in:
@@ -148,6 +148,24 @@ Features:
|
||||
hostname pattern and has a ProxyCommand set that allows connecting to any
|
||||
local VM/container that way without any networking configured.
|
||||
|
||||
* Varlinkification of the following command line tools, to open them up to
|
||||
other programs via IPC:
|
||||
- bootctl
|
||||
- journalctl (allowing journal read access via IPC)
|
||||
- coredumpcl
|
||||
- systemd-bless-boot
|
||||
- systemd-measure
|
||||
- systemd-creds (allowing clients to encrypt credentials locally)
|
||||
- systemd-cryptenroll (to allow UIs to enroll FIDO2 keys and such)
|
||||
- systemd-dissect
|
||||
- systemd-sysext
|
||||
- systemd-sysupdate
|
||||
- systemd-analyze
|
||||
- kernel-install
|
||||
|
||||
* Varlink: add glue code to allow varlink clients to be authenticated via
|
||||
Polkit by passing client pidfd over.
|
||||
|
||||
* in the service manager, pick up ERRNO= + BUSERROR= + VARLINKERROR= error
|
||||
identifiers, and store them along with the exit status of a server and report
|
||||
via "systemctl status".
|
||||
@@ -159,32 +177,15 @@ Features:
|
||||
* automatically mount one virtiofs during early boot phase to /run/host/,
|
||||
similar to how we do that for nspawn, based on some clear tag.
|
||||
|
||||
* make systemd-pcrextend accessible via varlink so that clients can measure
|
||||
things reasonably freely, and get a proper entry in the TPM event log we
|
||||
maintain, correctly synchronized
|
||||
|
||||
* add some service that makes an atomic snapshot of PCR state and event log up
|
||||
to that point available, possibly even with quote by the TPM.
|
||||
|
||||
* encode type1 entries in some UKI section to add additional entries to the
|
||||
menu.
|
||||
|
||||
* extend the various CLI tools we have that output JSON already to also read
|
||||
their command to execute from JSON in varlink format, so that you can fork
|
||||
them off and talk to them fully via varlink.
|
||||
|
||||
* add a "varlinkctl" tool that allows interacting with varlink services from
|
||||
the shell. In particular add a "--exec=" switch, which allows specifying a
|
||||
binary to invoke to which to talk via stdin/stdout
|
||||
|
||||
* make tools that speak varlink over stdin/stdout trivially sockect
|
||||
activatable. i.e. once bootctl, kernel-install, systemd-measure and similar
|
||||
speak varlink make them available via a .socket unit with Accept=yes, so that
|
||||
they can be talked to via IPC out-of-process
|
||||
|
||||
* beef up .service units that are socket activated with Accept=yes with options
|
||||
AllowPeerUser= + AllowPeerGroup= to allow trivially simple access control
|
||||
when invoked via socket as IPC services
|
||||
* Add ACL-based access management to .socket units. i.e. add AllowPeerUser= +
|
||||
AllowPeerGroup= that installs additional user/group ACL entries on AF_UNIX
|
||||
sockets.
|
||||
|
||||
* systemd-tpm2-setup should probably have a factory reset logic, i.e. when some
|
||||
kernel command line option is set we reset the TPM (equivalent of tpm2_clear
|
||||
@@ -929,8 +930,6 @@ Features:
|
||||
2. systemd-gpt-auto-generator should auto-mount /var if it finds it on disk
|
||||
3. mount.x-initrd mount option in fstab should be implied for /var
|
||||
|
||||
* implement varlink introspection
|
||||
|
||||
* make persistent restarts easier by adding a new setting OpenPersistentFile=
|
||||
or so, which allows opening one or more files that is "persistent" across
|
||||
service restarts, hot reboot, cold reboots (depending on configuration): the
|
||||
|
||||
@@ -582,6 +582,7 @@ o "/org/freedesktop/systemd1/job/42684"</programlisting>
|
||||
<citerefentry project='dbus'><refentrytitle>dbus-daemon</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<ulink url="https://www.freedesktop.org/wiki/Software/dbus">D-Bus</ulink>,
|
||||
<citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>varlinkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry project='die-net'><refentrytitle>wireshark</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
|
||||
@@ -1249,6 +1249,7 @@ manpages = [
|
||||
['systemd-user-runtime-dir', 'user-runtime-dir@.service'],
|
||||
''],
|
||||
['userdbctl', '1', [], 'ENABLE_USERDB'],
|
||||
['varlinkctl', '1', [], ''],
|
||||
['vconsole.conf', '5', [], 'ENABLE_VCONSOLE'],
|
||||
['veritytab', '5', [], 'HAVE_LIBCRYPTSETUP']
|
||||
]
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
<?xml version='1.0'?>
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
|
||||
<refentry id="varlinkctl"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>varlinkctl</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>varlinkctl</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>varlinkctl</refname>
|
||||
<refpurpose>Introspect with and invoke Varlink services</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>varlinkctl</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">info</arg>
|
||||
<arg choice="plain"><replaceable>ADDRESS</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>varlinkctl</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">list-interfaces</arg>
|
||||
<arg choice="plain"><replaceable>ADDRESS</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>varlinkctl</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">introspect</arg>
|
||||
<arg choice="plain"><replaceable>ADDRESS</replaceable></arg>
|
||||
<arg choice="plain"><replaceable>INTERFACE</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>varlinkctl</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">call</arg>
|
||||
<arg choice="plain"><replaceable>ADDRESS</replaceable></arg>
|
||||
<arg choice="plain"><replaceable>METHOD</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>PARAMETERS</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>varlinkctl</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">validate-idl</arg>
|
||||
<arg choice="opt"><replaceable>FILE</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>varlinkctl</command> may be used to introspect and invoke <ulink
|
||||
url="https://varlink.org/">Varlink</ulink> services.</para>
|
||||
|
||||
<para>Services are referenced by one of the following:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>A Varlink service reference starting with the <literal>unix:</literal> string, followed
|
||||
by an absolute <constant>AF_UNIX</constant> path, or by <literal>@</literal> and an arbitrary string
|
||||
(the latter for referencing sockets in the abstract namespace).</para></listitem>
|
||||
|
||||
<listitem><para>A Varlink service reference starting with the <literal>exec:</literal> string, followed
|
||||
by an absolute path of a binary to execute.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>For convenience these two simpler (redundant) service address syntaxes are also supported:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>A file system path to an <constant>AF_UNIX</constant> socket, either absolute
|
||||
(i.e. begins with <literal>/</literal>) or relative (in which case it must begin with
|
||||
<literal>./</literal>).</para></listitem>
|
||||
|
||||
<listitem><para>A file system path to an executable, either absolute or relative (as above, must begin
|
||||
with <literal>/</literal>, resp. <literal>./</literal>).</para></listitem>
|
||||
</itemizedlist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Commands</title>
|
||||
|
||||
<para>The following commands are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><command>info</command> <replaceable>ADDRESS</replaceable></term>
|
||||
|
||||
<listitem><para>Show brief information about the specified service, including vendor name and list of
|
||||
implemented interfaces. Expects a service address in the formats described above.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>list-interfaces</command> <replaceable>ADDRESS</replaceable></term>
|
||||
|
||||
<listitem><para>Show list of interfaces implemented by the specified service. Expects a service
|
||||
address in the formats described above.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>introspect</command> <replaceable>ADDRESS</replaceable> <replaceable>INTERFACE</replaceable></term>
|
||||
|
||||
<listitem><para>Show interface definition of the specified interface provided by the specified
|
||||
service. Expects a service address in the formats described above and a Varlink interface
|
||||
name.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>call</command> <replaceable>ADDRESS</replaceable> <replaceable>METHOD</replaceable> [<replaceable>ARGUMENTS</replaceable>]</term>
|
||||
|
||||
<listitem><para>Call the specified method of the specified service. Expects a service address in the
|
||||
format described above, a fully qualified Varlink method name, and a JSON arguments object. If the
|
||||
arguments object is not specified, it is read from STDIN instead. To pass an empty list of
|
||||
parameters, specify the empty object <literal>{}</literal>.</para>
|
||||
|
||||
<para>The reply parameters are written as JSON object to STDOUT.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>validate-idl</command> [<replaceable>FILE</replaceable>]</term>
|
||||
|
||||
<listitem><para>Reads a Varlink interface definition file, parses and validates it, then outputs it
|
||||
with syntax highlighting. This checks for syntax and internal consistency of the interface. Expects a
|
||||
file name to read the interface definition from. If omitted reads the interface definition from
|
||||
STDIN.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>help</command></term>
|
||||
|
||||
<listitem><para>Show command syntax help.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<para>The following options are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--more</option></term>
|
||||
|
||||
<listitem><para>When used with <command>call</command>: expect multiple method replies. If this flag is
|
||||
set the method call is sent with the <constant>more</constant> flag set, which tells the service to
|
||||
generate multiple replies, if needed. The command remains running until the service sends a reply
|
||||
message that indicates it is the last in the series. This flag should be set only for method calls
|
||||
that support this mechanism.</para>
|
||||
|
||||
<para>If this mode is enabled output is automatically switched to JSON-SEQ mode, so that individual
|
||||
reply objects can be easily discerned.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--oneway</option></term>
|
||||
|
||||
<listitem><para>When used with <command>call</command>: do not expect a method reply. If this flag
|
||||
is set the method call is sent with the <constant>oneway</constant> flag set (the command exits
|
||||
immediately after), which tells the service not to generate a reply.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--json=</option><replaceable>MODE</replaceable></term>
|
||||
|
||||
<listitem>
|
||||
<para>Selects the JSON output formatting, one of <literal>pretty</literal> (for nicely indented,
|
||||
colorized output) or <literal>short</literal> (for terse output with minimal whitespace and no
|
||||
newlines), defaults to <literal>short</literal>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-j</option></term>
|
||||
|
||||
<listitem>
|
||||
<para>Equivalent to <option>--json=pretty</option> when invoked interactively from a terminal. Otherwise
|
||||
equivalent to <option>--json=short</option>, in particular when the output is piped to some other
|
||||
program.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Examples</title>
|
||||
|
||||
<example>
|
||||
<title>Investigating a Service</title>
|
||||
|
||||
<para>The following three commands inspect the <literal>io.systemd.Resolve</literal> service
|
||||
implemented by
|
||||
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
listing general service information and implemented interfaces, and then displaying the interface
|
||||
definition of its primary interface:</para>
|
||||
|
||||
<programlisting>$ varlinkctl info /run/systemd/resolve/io.systemd.Resolve
|
||||
Vendor: The systemd Project
|
||||
Product: systemd (systemd-resolved)
|
||||
Version: 254 (254-1522-g4790521^)
|
||||
URL: https://systemd.io/
|
||||
Interfaces: io.systemd
|
||||
io.systemd.Resolve
|
||||
org.varlink.service
|
||||
$ varlinkctl list-interfaces /run/systemd/resolve/io.systemd.Resolve
|
||||
io.systemd
|
||||
io.systemd.Resolve
|
||||
org.varlink.service
|
||||
$ varlinkctl introspect /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve
|
||||
interface io.systemd.Resolve
|
||||
type ResolvedAddress(
|
||||
ifindex: ?int,
|
||||
…</programlisting>
|
||||
|
||||
<para>(Interface definition has been truncated in the example above, in the interest of brevity.)</para>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Invoking a Method</title>
|
||||
|
||||
<para>The following command resolves a hostname via <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s <function>ResolveHostname</function> method call.</para>
|
||||
|
||||
<programlisting>$ varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name":"systemd.io","family":2}' -j
|
||||
{
|
||||
"addresses" : [
|
||||
{
|
||||
"ifindex" : 2,
|
||||
"family" : 2,
|
||||
"address" : [
|
||||
185,
|
||||
199,
|
||||
111,
|
||||
153
|
||||
]
|
||||
}
|
||||
],
|
||||
"name" : "systemd.io",
|
||||
"flags" : 1048577
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Investigating a Service Executable</title>
|
||||
|
||||
<para>The following comand inspects the <filename>/usr/lib/systemd/systemd-pcrextend</filename>
|
||||
executable and the IPC APIs it provides. It then invokes a method on it:</para>
|
||||
|
||||
<programlisting># varlinkctl info /usr/lib/systemd/systemd-pcrextend
|
||||
Vendor: The systemd Project
|
||||
Product: systemd (systemd-pcrextend)
|
||||
Version: 254 (254-1536-g97734fb)
|
||||
URL: https://systemd.io/
|
||||
Interfaces: io.systemd
|
||||
io.systemd.PCRExtend
|
||||
org.varlink.service
|
||||
# varlinkctl introspect /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend
|
||||
interface io.systemd.PCRExtend
|
||||
|
||||
method Extend(
|
||||
pcr: int,
|
||||
text: ?string,
|
||||
data: ?string
|
||||
) -> ()
|
||||
# varlinkctl call /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend.Extend '{"pcr":15,"text":"foobar"}'
|
||||
{}</programlisting>
|
||||
</example>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
|
||||
<para>
|
||||
<citerefentry><refentrytitle>busctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<ulink url="https://varlink.org/">Varlink</ulink>
|
||||
</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
@@ -2193,6 +2193,7 @@ subdir('src/update-done')
|
||||
subdir('src/update-utmp')
|
||||
subdir('src/user-sessions')
|
||||
subdir('src/userdb')
|
||||
subdir('src/varlinkctl')
|
||||
subdir('src/vconsole')
|
||||
subdir('src/veritysetup')
|
||||
subdir('src/volatile-root')
|
||||
|
||||
@@ -254,4 +254,24 @@ static inline void free_many_charp(char **c, size_t n) {
|
||||
free_many((void**) c, n);
|
||||
}
|
||||
|
||||
_alloc_(2) static inline void *realloc0(void *p, size_t new_size) {
|
||||
size_t old_size;
|
||||
void *q;
|
||||
|
||||
/* Like realloc(), but initializes anything appended to zero */
|
||||
|
||||
old_size = MALLOC_SIZEOF_SAFE(p);
|
||||
|
||||
q = realloc(p, new_size);
|
||||
if (!q)
|
||||
return NULL;
|
||||
|
||||
new_size = MALLOC_SIZEOF_SAFE(q); /* Update with actually allocated space */
|
||||
|
||||
if (new_size > old_size)
|
||||
memset((uint8_t*) q + old_size, 0, new_size - old_size);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
#include "memory-util.h"
|
||||
|
||||
+10
-1
@@ -5,6 +5,8 @@
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
#include "varlink.h"
|
||||
#include "varlink-io.systemd.UserDatabase.h"
|
||||
#include "varlink-io.systemd.ManagedOOM.h"
|
||||
|
||||
typedef struct LookupParameters {
|
||||
const char *user_name;
|
||||
@@ -232,7 +234,7 @@ static int vl_method_subscribe_managed_oom_cgroups(
|
||||
/* We only take one subscriber for this method so return an error if there's already an existing one.
|
||||
* This shouldn't happen since systemd-oomd is the only client of this method. */
|
||||
if (FLAGS_SET(flags, VARLINK_METHOD_MORE) && m->managed_oom_varlink)
|
||||
return varlink_error(link, VARLINK_ERROR_SUBSCRIPTION_TAKEN, NULL);
|
||||
return varlink_error(link, "io.systemd.ManagedOOM.SubscriptionTaken", NULL);
|
||||
|
||||
r = build_managed_oom_cgroups_json(m, &v);
|
||||
if (r < 0)
|
||||
@@ -600,6 +602,13 @@ int manager_setup_varlink_server(Manager *m, VarlinkServer **ret) {
|
||||
|
||||
varlink_server_set_userdata(s, m);
|
||||
|
||||
r = varlink_server_add_interface_many(
|
||||
s,
|
||||
&vl_interface_io_systemd_UserDatabase,
|
||||
&vl_interface_io_systemd_ManagedOOM);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add interfaces to varlink server: %m");
|
||||
|
||||
r = varlink_server_bind_method_many(
|
||||
s,
|
||||
"io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "user-record-util.h"
|
||||
#include "user-record.h"
|
||||
#include "user-util.h"
|
||||
#include "varlink-io.systemd.UserDatabase.h"
|
||||
|
||||
/* Where to look for private/public keys that are used to sign the user records. We are not using
|
||||
* CONF_PATHS_NULSTR() here since we want to insert /var/lib/systemd/home/ in the middle. And we insert that
|
||||
@@ -1003,6 +1004,10 @@ static int manager_bind_varlink(Manager *m) {
|
||||
|
||||
varlink_server_set_userdata(m->varlink_server, m);
|
||||
|
||||
r = varlink_server_add_interface(m->varlink_server, &vl_interface_io_systemd_UserDatabase);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
|
||||
|
||||
r = varlink_server_bind_method_many(
|
||||
m->varlink_server,
|
||||
"io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
#include "syslog-util.h"
|
||||
#include "uid-alloc-range.h"
|
||||
#include "user-util.h"
|
||||
#include "varlink-io.systemd.Journal.h"
|
||||
|
||||
#define USER_JOURNALS_MAX 1024
|
||||
|
||||
@@ -2224,6 +2225,10 @@ static int server_open_varlink(Server *s, const char *socket, int fd) {
|
||||
|
||||
varlink_server_set_userdata(s->varlink_server, s);
|
||||
|
||||
r = varlink_server_add_interface(s->varlink_server, &vl_interface_io_systemd_Journal);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add Journal interface to varlink server: %m");
|
||||
|
||||
r = varlink_server_bind_method_many(
|
||||
s->varlink_server,
|
||||
"io.systemd.Journal.Synchronize", vl_method_synchronize,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "mkdir.h"
|
||||
#include "user-util.h"
|
||||
#include "varlink.h"
|
||||
#include "varlink-io.systemd.UserDatabase.h"
|
||||
|
||||
typedef struct LookupParameters {
|
||||
const char *user_name;
|
||||
@@ -392,6 +393,10 @@ int manager_varlink_init(Manager *m) {
|
||||
|
||||
varlink_server_set_userdata(s, m);
|
||||
|
||||
r = varlink_server_add_interface(s, &vl_interface_io_systemd_UserDatabase);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
|
||||
|
||||
r = varlink_server_bind_method_many(
|
||||
s,
|
||||
"io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "oomd-manager.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
#include "varlink-io.systemd.oom.h"
|
||||
|
||||
typedef struct ManagedOOMMessage {
|
||||
ManagedOOMMode mode;
|
||||
@@ -733,6 +734,10 @@ static int manager_varlink_init(Manager *m, int fd) {
|
||||
|
||||
varlink_server_set_userdata(s, m);
|
||||
|
||||
r = varlink_server_add_interface(s, &vl_interface_io_systemd_oom);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add oom interface to varlink server: %m");
|
||||
|
||||
r = varlink_server_bind_method(s, "io.systemd.oom.ReportManagedOOMCGroups", process_managed_oom_request);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to register varlink method: %m");
|
||||
|
||||
+147
-30
@@ -21,6 +21,8 @@
|
||||
#include "pretty-print.h"
|
||||
#include "tpm2-pcr.h"
|
||||
#include "tpm2-util.h"
|
||||
#include "varlink.h"
|
||||
#include "varlink-io.systemd.PCRExtend.h"
|
||||
|
||||
static bool arg_graceful = false;
|
||||
static char *arg_tpm2_device = NULL;
|
||||
@@ -28,11 +30,14 @@ static char **arg_banks = NULL;
|
||||
static char *arg_file_system = NULL;
|
||||
static bool arg_machine_id = false;
|
||||
static unsigned arg_pcr_index = UINT_MAX;
|
||||
static bool arg_varlink = false;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_file_system, freep);
|
||||
|
||||
#define EXTENSION_STRING_SAFE_LIMIT 1024
|
||||
|
||||
static int help(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
@@ -165,7 +170,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (arg_file_system && arg_machine_id)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--file-system= and --machine-id may not be combined.");
|
||||
|
||||
if (arg_pcr_index == UINT_MAX)
|
||||
r = varlink_invocation(VARLINK_ALLOW_ACCEPT);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
|
||||
if (r > 0)
|
||||
arg_varlink = true;
|
||||
else if (arg_pcr_index == UINT_MAX)
|
||||
arg_pcr_index = (arg_file_system || arg_machine_id) ?
|
||||
TPM2_PCR_SYSTEM_IDENTITY : /* → PCR 15 */
|
||||
TPM2_PCR_KERNEL_BOOT; /* → PCR 11 */
|
||||
@@ -257,10 +267,119 @@ static int get_file_system_word(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int extend_now(unsigned pcr, const void *data, size_t size, Tpm2UserspaceEventType event) {
|
||||
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
|
||||
int r;
|
||||
|
||||
r = tpm2_context_new(arg_tpm2_device, &c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = determine_banks(c, pcr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (strv_isempty(arg_banks)) /* Still none? */
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Found a TPM2 without enabled PCR banks. Can't operate.");
|
||||
|
||||
_cleanup_free_ char *joined_banks = NULL;
|
||||
joined_banks = strv_join(arg_banks, ", ");
|
||||
if (!joined_banks)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_free_ char *safe = NULL;
|
||||
if (size > EXTENSION_STRING_SAFE_LIMIT) {
|
||||
safe = cescape_length(data, EXTENSION_STRING_SAFE_LIMIT);
|
||||
if (!safe)
|
||||
return log_oom();
|
||||
|
||||
if (!strextend(&safe, "..."))
|
||||
return log_oom();
|
||||
} else {
|
||||
safe = cescape_length(data, size);
|
||||
if (!safe)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
log_debug("Measuring '%s' into PCR index %u, banks %s.", safe, pcr, joined_banks);
|
||||
|
||||
r = tpm2_extend_bytes(c, arg_banks, pcr, data, size, /* secret= */ NULL, /* secret_size= */ 0, event, safe);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not extend PCR: %m");
|
||||
|
||||
log_struct(LOG_INFO,
|
||||
"MESSAGE_ID=" SD_MESSAGE_TPM_PCR_EXTEND_STR,
|
||||
LOG_MESSAGE("Extended PCR index %u with '%s' (banks %s).", pcr, safe, joined_banks),
|
||||
"MEASURING=%s", safe,
|
||||
"PCR=%u", pcr,
|
||||
"BANKS=%s", joined_banks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct MethodExtendParameters {
|
||||
unsigned pcr;
|
||||
const char *text;
|
||||
void *data;
|
||||
size_t data_size;
|
||||
} MethodExtendParameters;
|
||||
|
||||
static int json_dispatch_binary_data(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
|
||||
MethodExtendParameters *p = ASSERT_PTR(userdata);
|
||||
_cleanup_free_ void *d = NULL;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
r = json_variant_unbase64(variant, &d, &l);
|
||||
if (r < 0)
|
||||
return json_log(variant, flags, r, "JSON variant is not a base64 string.");
|
||||
|
||||
free_and_replace(p->data, d);
|
||||
p->data_size = l;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vl_method_extend(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
||||
|
||||
static const JsonDispatch dispatch_table[] = {
|
||||
{ "pcr", JSON_VARIANT_UNSIGNED, json_dispatch_uint, offsetof(MethodExtendParameters, pcr), JSON_MANDATORY },
|
||||
{ "text", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodExtendParameters, text), 0 },
|
||||
{ "data", JSON_VARIANT_STRING, json_dispatch_binary_data, 0, 0 },
|
||||
{}
|
||||
};
|
||||
MethodExtendParameters p = {
|
||||
.pcr = UINT_MAX,
|
||||
};
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!TPM2_PCR_INDEX_VALID(p.pcr))
|
||||
return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "pcr")));
|
||||
|
||||
if (p.text) {
|
||||
/* Specifying both the text string and the binary data is not allowed */
|
||||
if (p.data)
|
||||
return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "data")));
|
||||
|
||||
r = extend_now(p.pcr, p.text, strlen(p.text), _TPM2_USERSPACE_EVENT_TYPE_INVALID);
|
||||
} else if (p.data)
|
||||
r = extend_now(p.pcr, p.data, p.data_size, _TPM2_USERSPACE_EVENT_TYPE_INVALID);
|
||||
else
|
||||
return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "text")));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return varlink_reply(link, NULL);
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_free_ char *joined = NULL, *word = NULL;
|
||||
_cleanup_free_ char *word = NULL;
|
||||
Tpm2UserspaceEventType event;
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
@@ -269,6 +388,30 @@ static int run(int argc, char *argv[]) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (arg_varlink) {
|
||||
_cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL;
|
||||
|
||||
/* Invocation as Varlink service */
|
||||
|
||||
r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate Varlink server: %m");
|
||||
|
||||
r = varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_PCRExtend);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add Varlink interface: %m");
|
||||
|
||||
r = varlink_server_bind_method(varlink_server, "io.systemd.PCRExtend.Extend", vl_method_extend);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind Varlink method: %m");
|
||||
|
||||
r = varlink_server_loop_auto(varlink_server);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run Varlink event loop: %m");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (arg_file_system) {
|
||||
_cleanup_free_ char *normalized = NULL, *normalized_escaped = NULL;
|
||||
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
|
||||
@@ -348,8 +491,6 @@ static int run(int argc, char *argv[]) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
length = strlen(word);
|
||||
|
||||
/* Skip logic if sd-stub is not used, after all PCR 11 might have a very different purpose then. */
|
||||
r = efi_measured_uki(LOG_ERR);
|
||||
if (r < 0)
|
||||
@@ -359,34 +500,10 @@ static int run(int argc, char *argv[]) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
|
||||
r = tpm2_context_new(arg_tpm2_device, &c);
|
||||
r = extend_now(arg_pcr_index, word, strlen(word), event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create TPM2 context: %m");
|
||||
|
||||
r = determine_banks(c, arg_pcr_index);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (strv_isempty(arg_banks)) /* Still none? */
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Found a TPM2 without enabled PCR banks. Can't operate.");
|
||||
|
||||
joined = strv_join(arg_banks, ", ");
|
||||
if (!joined)
|
||||
return log_oom();
|
||||
|
||||
log_debug("Measuring '%s' into PCR index %u, banks %s.", word, arg_pcr_index, joined);
|
||||
|
||||
r = tpm2_extend_bytes(c, arg_banks, arg_pcr_index, word, length, NULL, 0, event, word);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not extend PCR: %m");
|
||||
|
||||
log_struct(LOG_INFO,
|
||||
"MESSAGE_ID=" SD_MESSAGE_TPM_PCR_EXTEND_STR,
|
||||
LOG_MESSAGE("Extended PCR index %u with '%s' (banks %s).", arg_pcr_index, word, joined),
|
||||
"MEASURING=%s", word,
|
||||
"PCR=%u", arg_pcr_index,
|
||||
"BANKS=%s", joined);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "resolved-dns-synthesize.h"
|
||||
#include "resolved-varlink.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "varlink-io.systemd.Resolve.h"
|
||||
#include "varlink-io.systemd.Resolve.Monitor.h"
|
||||
|
||||
typedef struct LookupParameters {
|
||||
int ifindex;
|
||||
@@ -699,6 +701,10 @@ static int varlink_monitor_server_init(Manager *m) {
|
||||
|
||||
varlink_server_set_userdata(server, m);
|
||||
|
||||
r = varlink_server_add_interface(server, &vl_interface_io_systemd_Resolve_Monitor);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add Resolve.Monitor interface to varlink server: %m");
|
||||
|
||||
r = varlink_server_bind_method_many(
|
||||
server,
|
||||
"io.systemd.Resolve.Monitor.SubscribeQueryResults", vl_method_subscribe_query_results,
|
||||
@@ -741,6 +747,10 @@ static int varlink_main_server_init(Manager *m) {
|
||||
|
||||
varlink_server_set_userdata(s, m);
|
||||
|
||||
r = varlink_server_add_interface(s, &vl_interface_io_systemd_Resolve);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add Resolve interface to varlink server: %m");
|
||||
|
||||
r = varlink_server_bind_method_many(
|
||||
s,
|
||||
"io.systemd.Resolve.ResolveHostname", vl_method_resolve_hostname,
|
||||
|
||||
@@ -166,6 +166,16 @@ shared_sources = files(
|
||||
'userdb-dropin.c',
|
||||
'userdb.c',
|
||||
'varlink.c',
|
||||
'varlink-idl.c',
|
||||
'varlink-io.systemd.c',
|
||||
'varlink-io.systemd.Journal.c',
|
||||
'varlink-io.systemd.ManagedOOM.c',
|
||||
'varlink-io.systemd.PCRExtend.c',
|
||||
'varlink-io.systemd.Resolve.Monitor.c',
|
||||
'varlink-io.systemd.Resolve.c',
|
||||
'varlink-io.systemd.UserDatabase.c',
|
||||
'varlink-io.systemd.oom.c',
|
||||
'varlink-org.varlink.service.c',
|
||||
'verb-log-control.c',
|
||||
'verbs.c',
|
||||
'vlan-util.c',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,158 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "json.h"
|
||||
#include "macro.h"
|
||||
|
||||
/* This implements the Varlink Interface Definition Language ("Varlink IDL"),
|
||||
* i.e. https://varlink.org/Interface-Definition
|
||||
*
|
||||
* Primarily allows encoding static interface definitions in C code, that can be converted to the textual IDL
|
||||
* format on-the-fly. Can also parse the textual format back to C structures. Validates the interface
|
||||
* definitions for internal consistency and validates JSON objects against the interface definitions. */
|
||||
|
||||
typedef enum VarlinkSymbolType {
|
||||
VARLINK_ENUM_TYPE,
|
||||
VARLINK_STRUCT_TYPE,
|
||||
VARLINK_METHOD,
|
||||
VARLINK_ERROR,
|
||||
_VARLINK_SYMBOL_TYPE_MAX,
|
||||
_VARLINK_SYMBOL_TYPE_INVALID = -EINVAL,
|
||||
} VarlinkSymbolType;
|
||||
|
||||
typedef enum VarlinkFieldType {
|
||||
_VARLINK_FIELD_TYPE_END_MARKER = 0, /* zero type means: this is the last entry in the fields[] array of VarlinkSymbol */
|
||||
VARLINK_STRUCT,
|
||||
VARLINK_ENUM,
|
||||
VARLINK_NAMED_TYPE,
|
||||
VARLINK_BOOL,
|
||||
VARLINK_INT,
|
||||
VARLINK_FLOAT,
|
||||
VARLINK_STRING,
|
||||
VARLINK_OBJECT,
|
||||
VARLINK_ENUM_VALUE,
|
||||
_VARLINK_FIELD_TYPE_MAX,
|
||||
_VARLINK_FIELD_TYPE_INVALID = -EINVAL,
|
||||
} VarlinkFieldType;
|
||||
|
||||
typedef enum VarlinkFieldDirection {
|
||||
VARLINK_REGULAR,
|
||||
VARLINK_INPUT,
|
||||
VARLINK_OUTPUT,
|
||||
_VARLINK_FIELD_DIRECTION_MAX,
|
||||
_VARLINK_FIELD_DIRECTION_INVALID = -EINVAL,
|
||||
} VarlinkFieldDirection;
|
||||
|
||||
typedef enum VarlinkFieldFlags {
|
||||
VARLINK_ARRAY = 1 << 0,
|
||||
VARLINK_MAP = 1 << 1,
|
||||
VARLINK_NULLABLE = 1 << 2,
|
||||
_VARLINK_FIELD_FLAGS_MAX = (1 << 3) - 1,
|
||||
_VARLINK_FIELD_FLAGS_INVALID = -EINVAL,
|
||||
} VarlinkFieldFlags;
|
||||
|
||||
typedef struct VarlinkField VarlinkField;
|
||||
typedef struct VarlinkSymbol VarlinkSymbol;
|
||||
typedef struct VarlinkInterface VarlinkInterface;
|
||||
|
||||
/* Fields are the components making up symbols */
|
||||
struct VarlinkField {
|
||||
const char *name;
|
||||
VarlinkFieldType field_type;
|
||||
VarlinkFieldFlags field_flags;
|
||||
VarlinkFieldDirection field_direction; /* in case of method call fields: whether input or output argument */
|
||||
const VarlinkSymbol *symbol; /* VARLINK_STRUCT, VARLINK_ENUM: anonymous symbol that carries the definitions, VARLINK_NAMED_TYPE: resolved symbol */
|
||||
const char *named_type; /* VARLINK_NAMED_TYPE */
|
||||
};
|
||||
|
||||
/* Symbols are primary named concepts in an interface, and are methods, errors or named types (either enum or struct). */
|
||||
struct VarlinkSymbol {
|
||||
const char *name; /* most symbols have a name, but sometimes they are created on-the-fly for fields, in which case they are anonymous */
|
||||
VarlinkSymbolType symbol_type;
|
||||
VarlinkField fields[];
|
||||
};
|
||||
|
||||
/* An interface definition has a name and consist of symbols */
|
||||
struct VarlinkInterface {
|
||||
const char *name;
|
||||
const VarlinkSymbol *symbols[];
|
||||
};
|
||||
|
||||
#define VARLINK_DEFINE_FIELD(_name, _field_type, _field_flags) \
|
||||
{ .name = #_name, .field_type = (_field_type), .field_flags = (_field_flags) }
|
||||
|
||||
#define VARLINK_DEFINE_FIELD_BY_TYPE(_name, _named_type, _field_flags) \
|
||||
{ .name = #_name, .field_type = VARLINK_NAMED_TYPE, .named_type = #_named_type, .symbol = &vl_type_ ## _named_type, .field_flags = (_field_flags) }
|
||||
|
||||
#define VARLINK_DEFINE_INPUT(_name, _field_type, _field_flags) \
|
||||
{ .name = #_name, .field_type = (_field_type), .field_flags = (_field_flags), .field_direction = VARLINK_INPUT }
|
||||
|
||||
#define VARLINK_DEFINE_INPUT_BY_TYPE(_name, _named_type, _field_flags) \
|
||||
{ .name = #_name, .field_type = VARLINK_NAMED_TYPE, .named_type = #_named_type, .symbol = &vl_type_ ## _named_type, .field_flags = (_field_flags), .field_direction = VARLINK_INPUT }
|
||||
|
||||
#define VARLINK_DEFINE_OUTPUT(_name, _field_type, _field_flags) \
|
||||
{ .name = #_name, .field_type = (_field_type), .field_flags = (_field_flags), .field_direction = VARLINK_OUTPUT }
|
||||
|
||||
#define VARLINK_DEFINE_OUTPUT_BY_TYPE(_name, _named_type, _field_flags) \
|
||||
{ .name = #_name, .field_type = VARLINK_NAMED_TYPE, .named_type = #_named_type, .symbol = &vl_type_ ## _named_type, .field_flags = (_field_flags), .field_direction = VARLINK_OUTPUT }
|
||||
|
||||
#define VARLINK_DEFINE_ENUM_VALUE(_name) \
|
||||
{ .name = #_name, .field_type = VARLINK_ENUM_VALUE }
|
||||
|
||||
#define VARLINK_DEFINE_METHOD(_name, ...) \
|
||||
const VarlinkSymbol vl_method_ ## _name = { \
|
||||
.name = #_name, \
|
||||
.symbol_type = VARLINK_METHOD, \
|
||||
.fields = { __VA_ARGS__ __VA_OPT__(,) {}}, \
|
||||
}
|
||||
|
||||
#define VARLINK_DEFINE_ERROR(_name, ...) \
|
||||
const VarlinkSymbol vl_error_ ## _name = { \
|
||||
.name = #_name, \
|
||||
.symbol_type = VARLINK_ERROR, \
|
||||
.fields = { __VA_ARGS__ __VA_OPT__(,) {}}, \
|
||||
}
|
||||
|
||||
#define VARLINK_DEFINE_STRUCT_TYPE(_name, ...) \
|
||||
const VarlinkSymbol vl_type_ ## _name = { \
|
||||
.name = #_name, \
|
||||
.symbol_type = VARLINK_STRUCT_TYPE, \
|
||||
.fields = { __VA_ARGS__ __VA_OPT__(,) {}}, \
|
||||
}
|
||||
|
||||
#define VARLINK_DEFINE_ENUM_TYPE(_name, ...) \
|
||||
const VarlinkSymbol vl_type_ ## _name = { \
|
||||
.name = #_name, \
|
||||
.symbol_type = VARLINK_ENUM_TYPE, \
|
||||
.fields = { __VA_ARGS__ __VA_OPT__(,) {}}, \
|
||||
}
|
||||
|
||||
#define VARLINK_DEFINE_INTERFACE(_name, _full_name, ...) \
|
||||
const VarlinkInterface vl_interface_ ## _name = { \
|
||||
.name = (_full_name), \
|
||||
.symbols = { __VA_ARGS__ __VA_OPT__(,) NULL}, \
|
||||
}
|
||||
|
||||
int varlink_idl_dump(FILE *f, int use_colors, const VarlinkInterface *interface);
|
||||
int varlink_idl_format(const VarlinkInterface *interface, char **ret);
|
||||
|
||||
int varlink_idl_parse(const char *text, unsigned *ret_line, unsigned *ret_column, VarlinkInterface **ret);
|
||||
VarlinkInterface* varlink_interface_free(VarlinkInterface *interface);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkInterface*, varlink_interface_free);
|
||||
|
||||
bool varlink_idl_field_name_is_valid(const char *name);
|
||||
bool varlink_idl_symbol_name_is_valid(const char *name);
|
||||
bool varlink_idl_interface_name_is_valid(const char *name);
|
||||
|
||||
int varlink_idl_consistent(const VarlinkInterface *interface, bool level);
|
||||
|
||||
const VarlinkSymbol* varlink_idl_find_symbol(const VarlinkInterface *interface, VarlinkSymbolType type, const char *name);
|
||||
const VarlinkField* varlink_idl_find_field(const VarlinkSymbol *symbol, const char *name);
|
||||
|
||||
int varlink_idl_validate_method_call(const VarlinkSymbol *method, JsonVariant *v, const char **bad_field);
|
||||
int varlink_idl_validate_method_reply(const VarlinkSymbol *method, JsonVariant *v, const char **bad_field);
|
||||
int varlink_idl_validate_error(const VarlinkSymbol *error, JsonVariant *v, const char **bad_field);
|
||||
@@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "varlink-io.systemd.Journal.h"
|
||||
|
||||
static VARLINK_DEFINE_METHOD(Synchronize);
|
||||
static VARLINK_DEFINE_METHOD(Rotate);
|
||||
static VARLINK_DEFINE_METHOD(FlushToVar);
|
||||
static VARLINK_DEFINE_METHOD(RelinquishVar);
|
||||
|
||||
static VARLINK_DEFINE_ERROR(NotSupportedByNamespaces);
|
||||
|
||||
VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_Journal,
|
||||
"io.systemd.Journal",
|
||||
&vl_method_Synchronize,
|
||||
&vl_method_Rotate,
|
||||
&vl_method_FlushToVar,
|
||||
&vl_method_RelinquishVar,
|
||||
&vl_error_NotSupportedByNamespaces);
|
||||
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "varlink-idl.h"
|
||||
|
||||
extern const VarlinkInterface vl_interface_io_systemd_Journal;
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "varlink-io.systemd.ManagedOOM.h"
|
||||
|
||||
/* Pull in vl_type_ControlGroup, since both interfaces need it */
|
||||
#include "varlink-io.systemd.oom.h"
|
||||
|
||||
/* This is PID1's Varlink service, where PID 1 is the server and oomd is the client.
|
||||
*
|
||||
* Compare with io.systemd.oom where the client/server roles of oomd and the service manager are swapped! */
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
SubscribeManagedOOMCGroups,
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(cgroups, ControlGroup, VARLINK_ARRAY));
|
||||
|
||||
static VARLINK_DEFINE_ERROR(SubscriptionTaken);
|
||||
|
||||
VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_ManagedOOM,
|
||||
"io.systemd.ManagedOOM",
|
||||
&vl_method_SubscribeManagedOOMCGroups,
|
||||
&vl_type_ControlGroup,
|
||||
&vl_error_SubscriptionTaken);
|
||||
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "varlink-idl.h"
|
||||
|
||||
extern const VarlinkInterface vl_interface_io_systemd_ManagedOOM;
|
||||
@@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "varlink-io.systemd.PCRExtend.h"
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
Extend,
|
||||
VARLINK_DEFINE_INPUT(pcr, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_INPUT(text, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_INPUT(data, VARLINK_STRING, VARLINK_NULLABLE));
|
||||
|
||||
VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_PCRExtend,
|
||||
"io.systemd.PCRExtend",
|
||||
&vl_method_Extend);
|
||||
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "varlink-idl.h"
|
||||
|
||||
extern const VarlinkInterface vl_interface_io_systemd_PCRExtend;
|
||||
@@ -0,0 +1,136 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "varlink-io.systemd.Resolve.Monitor.h"
|
||||
|
||||
VARLINK_DEFINE_STRUCT_TYPE(
|
||||
ResourceKey,
|
||||
VARLINK_DEFINE_FIELD(class, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(type, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(name, VARLINK_STRING, 0));
|
||||
|
||||
VARLINK_DEFINE_STRUCT_TYPE(
|
||||
ResourceRecord,
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(key, ResourceKey, 0),
|
||||
VARLINK_DEFINE_FIELD(priority, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(weight, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(port, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(name, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(cpu, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(os, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(items, VARLINK_STRING, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(address, VARLINK_INT, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(mname, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(rname, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(serial, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(refresh, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(expire, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(minimum, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(exchange, VARLINK_STRING, VARLINK_NULLABLE));
|
||||
|
||||
VARLINK_DEFINE_STRUCT_TYPE(
|
||||
Answer,
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(rr, ResourceRecord, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(raw, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_FIELD(ifindex, VARLINK_INT, VARLINK_NULLABLE));
|
||||
|
||||
VARLINK_DEFINE_METHOD(
|
||||
SubscribeQueryResults,
|
||||
/* First reply */
|
||||
VARLINK_DEFINE_OUTPUT(ready, VARLINK_BOOL, VARLINK_NULLABLE),
|
||||
/* Subsequent replies */
|
||||
VARLINK_DEFINE_OUTPUT(state, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_OUTPUT(rcode, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_OUTPUT(errno, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(question, ResourceKey, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(collectedQuestions, ResourceKey, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(answer, Answer, VARLINK_NULLABLE|VARLINK_ARRAY));
|
||||
|
||||
VARLINK_DEFINE_STRUCT_TYPE(
|
||||
CacheEntry,
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(key, ResourceKey, 0),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(rrs, ResourceRecord, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(type, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(until, VARLINK_INT, 0));
|
||||
|
||||
VARLINK_DEFINE_STRUCT_TYPE(
|
||||
ScopeCache,
|
||||
VARLINK_DEFINE_FIELD(protocol, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_FIELD(family, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(ifindex, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(ifname, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(cache, CacheEntry, VARLINK_ARRAY));
|
||||
|
||||
VARLINK_DEFINE_METHOD(
|
||||
DumpCache,
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(dump, ScopeCache, VARLINK_ARRAY));
|
||||
|
||||
VARLINK_DEFINE_STRUCT_TYPE(
|
||||
ServerState,
|
||||
VARLINK_DEFINE_FIELD(Server, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_FIELD(Type, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_FIELD(Interface, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(InterfaceIndex, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(VerifiedFeatureLevel, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_FIELD(PossibleFeatureLevel, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_FIELD(DNSSECMode, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_FIELD(DNSSECSupported, VARLINK_BOOL, 0),
|
||||
VARLINK_DEFINE_FIELD(ReceivedUDPFragmentMax, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(FailedUDPAttempts, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(FailedTCPAttempts, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(PacketTruncated, VARLINK_BOOL, 0),
|
||||
VARLINK_DEFINE_FIELD(PacketBadOpt, VARLINK_BOOL, 0),
|
||||
VARLINK_DEFINE_FIELD(PacketRRSIGMissing, VARLINK_BOOL, 0),
|
||||
VARLINK_DEFINE_FIELD(PacketInvalid, VARLINK_BOOL, 0),
|
||||
VARLINK_DEFINE_FIELD(PacketDoOff, VARLINK_BOOL, 0));
|
||||
|
||||
VARLINK_DEFINE_METHOD(
|
||||
DumpServerState,
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(dump, ServerState, VARLINK_ARRAY));
|
||||
|
||||
VARLINK_DEFINE_STRUCT_TYPE(
|
||||
TransactionStatistics,
|
||||
VARLINK_DEFINE_FIELD(currentTransactions, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(totalTransactions, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(totalTimeouts, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(totalTimeoutsServedStale, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(totalFailedResponses, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(totalFailedResponsesServedStale, VARLINK_INT, 0));
|
||||
|
||||
VARLINK_DEFINE_STRUCT_TYPE(
|
||||
CacheStatistics,
|
||||
VARLINK_DEFINE_FIELD(size, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(hits, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(misses, VARLINK_INT, 0));
|
||||
|
||||
VARLINK_DEFINE_STRUCT_TYPE(
|
||||
DnssecStatistics,
|
||||
VARLINK_DEFINE_FIELD(secure, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(insecure, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(bogus, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(indeterminate, VARLINK_INT, 0));
|
||||
|
||||
VARLINK_DEFINE_METHOD(
|
||||
DumpStatistics,
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(transactions, TransactionStatistics, 0),
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(cache, CacheStatistics, 0),
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(dnssec, DnssecStatistics, 0));
|
||||
|
||||
VARLINK_DEFINE_METHOD(ResetStatistics);
|
||||
|
||||
VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_Resolve_Monitor,
|
||||
"io.systemd.Resolve.Monitor",
|
||||
&vl_method_SubscribeQueryResults,
|
||||
&vl_method_DumpCache,
|
||||
&vl_method_DumpServerState,
|
||||
&vl_method_DumpStatistics,
|
||||
&vl_method_ResetStatistics,
|
||||
&vl_type_ResourceKey,
|
||||
&vl_type_ResourceRecord,
|
||||
&vl_type_Answer,
|
||||
&vl_type_CacheEntry,
|
||||
&vl_type_ScopeCache,
|
||||
&vl_type_TransactionStatistics,
|
||||
&vl_type_CacheStatistics,
|
||||
&vl_type_DnssecStatistics,
|
||||
&vl_type_ServerState);
|
||||
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "varlink-idl.h"
|
||||
|
||||
extern const VarlinkInterface vl_interface_io_systemd_Resolve_Monitor;
|
||||
@@ -0,0 +1,76 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "varlink-io.systemd.Resolve.h"
|
||||
|
||||
static VARLINK_DEFINE_STRUCT_TYPE(
|
||||
ResolvedAddress,
|
||||
VARLINK_DEFINE_FIELD(ifindex, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(family, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(address, VARLINK_INT, VARLINK_ARRAY));
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
ResolveHostname,
|
||||
VARLINK_DEFINE_INPUT(ifindex, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_INPUT(name, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_INPUT(family, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_INPUT(flags, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(addresses, ResolvedAddress, VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_OUTPUT(name, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_OUTPUT(flags, VARLINK_INT, 0));
|
||||
|
||||
static VARLINK_DEFINE_STRUCT_TYPE(
|
||||
ResolvedName,
|
||||
VARLINK_DEFINE_FIELD(ifindex, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(name, VARLINK_STRING, 0));
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
ResolveAddress,
|
||||
VARLINK_DEFINE_INPUT(ifindex, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_INPUT(family, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_INPUT(address, VARLINK_INT, VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_INPUT(flags, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(names, ResolvedName, VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_OUTPUT(flags, VARLINK_INT, 0));
|
||||
|
||||
static VARLINK_DEFINE_ERROR(NoNameServers);
|
||||
static VARLINK_DEFINE_ERROR(NoSuchResourceRecord);
|
||||
static VARLINK_DEFINE_ERROR(QueryTimedOut);
|
||||
static VARLINK_DEFINE_ERROR(MaxAttemptsReached);
|
||||
static VARLINK_DEFINE_ERROR(InvalidReply);
|
||||
static VARLINK_DEFINE_ERROR(QueryAborted);
|
||||
static VARLINK_DEFINE_ERROR(
|
||||
DNSSECValidationFailed,
|
||||
VARLINK_DEFINE_FIELD(result, VARLINK_STRING, 0));
|
||||
static VARLINK_DEFINE_ERROR(NoTrustAnchor);
|
||||
static VARLINK_DEFINE_ERROR(ResourceRecordTypeUnsupported);
|
||||
static VARLINK_DEFINE_ERROR(NetworkDown);
|
||||
static VARLINK_DEFINE_ERROR(NoSource);
|
||||
static VARLINK_DEFINE_ERROR(StubLoop);
|
||||
static VARLINK_DEFINE_ERROR(
|
||||
DNSError,
|
||||
VARLINK_DEFINE_FIELD(rcode, VARLINK_INT, 0));
|
||||
static VARLINK_DEFINE_ERROR(CNAMELoop);
|
||||
static VARLINK_DEFINE_ERROR(BadAddressSize);
|
||||
|
||||
VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_Resolve,
|
||||
"io.systemd.Resolve",
|
||||
&vl_method_ResolveHostname,
|
||||
&vl_method_ResolveAddress,
|
||||
&vl_type_ResolvedAddress,
|
||||
&vl_type_ResolvedName,
|
||||
&vl_error_NoNameServers,
|
||||
&vl_error_NoSuchResourceRecord,
|
||||
&vl_error_QueryTimedOut,
|
||||
&vl_error_MaxAttemptsReached,
|
||||
&vl_error_InvalidReply,
|
||||
&vl_error_QueryAborted,
|
||||
&vl_error_DNSSECValidationFailed,
|
||||
&vl_error_NoTrustAnchor,
|
||||
&vl_error_ResourceRecordTypeUnsupported,
|
||||
&vl_error_NetworkDown,
|
||||
&vl_error_NoSource,
|
||||
&vl_error_StubLoop,
|
||||
&vl_error_DNSError,
|
||||
&vl_error_CNAMELoop,
|
||||
&vl_error_BadAddressSize);
|
||||
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "varlink-idl.h"
|
||||
|
||||
extern const VarlinkInterface vl_interface_io_systemd_Resolve;
|
||||
@@ -0,0 +1,46 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "varlink-io.systemd.UserDatabase.h"
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
GetUserRecord,
|
||||
VARLINK_DEFINE_INPUT(uid, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_INPUT(userName, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_INPUT(service, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_OUTPUT(record, VARLINK_OBJECT, 0),
|
||||
VARLINK_DEFINE_OUTPUT(incomplete, VARLINK_BOOL, 0));
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
GetGroupRecord,
|
||||
VARLINK_DEFINE_INPUT(gid, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_INPUT(groupName, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_INPUT(service, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_OUTPUT(record, VARLINK_OBJECT, 0),
|
||||
VARLINK_DEFINE_OUTPUT(incomplete, VARLINK_BOOL, 0));
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
GetMemberships,
|
||||
VARLINK_DEFINE_INPUT(userName, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_INPUT(groupName, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_INPUT(service, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_OUTPUT(userName, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_OUTPUT(groupName, VARLINK_STRING, 0));
|
||||
|
||||
static VARLINK_DEFINE_ERROR(NoRecordFound);
|
||||
static VARLINK_DEFINE_ERROR(BadService);
|
||||
static VARLINK_DEFINE_ERROR(ServiceNotAvailable);
|
||||
static VARLINK_DEFINE_ERROR(ConflictingRecordNotFound);
|
||||
static VARLINK_DEFINE_ERROR(EnumerationNotSupported);
|
||||
|
||||
/* As per https://systemd.io/USER_GROUP_API/ */
|
||||
VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_UserDatabase,
|
||||
"io.systemd.UserDatabase",
|
||||
&vl_method_GetUserRecord,
|
||||
&vl_method_GetGroupRecord,
|
||||
&vl_method_GetMemberships,
|
||||
&vl_error_NoRecordFound,
|
||||
&vl_error_BadService,
|
||||
&vl_error_ServiceNotAvailable,
|
||||
&vl_error_ConflictingRecordNotFound,
|
||||
&vl_error_EnumerationNotSupported);
|
||||
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "varlink-idl.h"
|
||||
|
||||
extern const VarlinkInterface vl_interface_io_systemd_UserDatabase;
|
||||
@@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "varlink-io.systemd.h"
|
||||
|
||||
/* These are local errors that never cross the wire, and are our own invention */
|
||||
static VARLINK_DEFINE_ERROR(Disconnected);
|
||||
static VARLINK_DEFINE_ERROR(TimedOut);
|
||||
static VARLINK_DEFINE_ERROR(Protocol);
|
||||
|
||||
/* This one we invented, and use for generically propagating system errors (errno) to clients */
|
||||
static VARLINK_DEFINE_ERROR(
|
||||
System,
|
||||
VARLINK_DEFINE_FIELD(errno, VARLINK_INT, 0));
|
||||
|
||||
VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd,
|
||||
"io.systemd",
|
||||
&vl_error_Disconnected,
|
||||
&vl_error_TimedOut,
|
||||
&vl_error_Protocol,
|
||||
&vl_error_System);
|
||||
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "varlink-idl.h"
|
||||
|
||||
extern const VarlinkInterface vl_interface_io_systemd;
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "varlink-io.systemd.oom.h"
|
||||
|
||||
/* This is oomd's Varlink service, where oomd is server and systemd --user is the client.
|
||||
*
|
||||
* Compare with io.systemd.ManagedOOM where the client/server roles of the service manager and oomd are
|
||||
* swapped! */
|
||||
|
||||
VARLINK_DEFINE_STRUCT_TYPE(
|
||||
ControlGroup,
|
||||
VARLINK_DEFINE_FIELD(mode, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_FIELD(path, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_FIELD(property, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_FIELD(limit, VARLINK_INT, VARLINK_NULLABLE));
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
ReportManagedOOMCGroups,
|
||||
VARLINK_DEFINE_INPUT_BY_TYPE(cgroups, ControlGroup, VARLINK_ARRAY));
|
||||
|
||||
VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_oom,
|
||||
"io.systemd.oom",
|
||||
&vl_method_ReportManagedOOMCGroups,
|
||||
&vl_type_ControlGroup);
|
||||
@@ -0,0 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "varlink-idl.h"
|
||||
|
||||
extern const VarlinkSymbol vl_type_ControlGroup;
|
||||
extern const VarlinkInterface vl_interface_io_systemd_oom;
|
||||
@@ -0,0 +1,45 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "varlink-org.varlink.service.h"
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
GetInfo,
|
||||
VARLINK_DEFINE_OUTPUT(vendor, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_OUTPUT(product, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_OUTPUT(version, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_OUTPUT(url, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_OUTPUT(interfaces, VARLINK_STRING, VARLINK_ARRAY));
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
GetInterfaceDescription,
|
||||
VARLINK_DEFINE_OUTPUT(interface, VARLINK_STRING, 0));
|
||||
|
||||
static VARLINK_DEFINE_ERROR(
|
||||
InterfaceNotFound,
|
||||
VARLINK_DEFINE_FIELD(interface, VARLINK_STRING, 0));
|
||||
|
||||
static VARLINK_DEFINE_ERROR(
|
||||
MethodNotFound,
|
||||
VARLINK_DEFINE_FIELD(method, VARLINK_STRING, 0));
|
||||
|
||||
static VARLINK_DEFINE_ERROR(
|
||||
MethodNotImplemented,
|
||||
VARLINK_DEFINE_FIELD(method, VARLINK_STRING, 0));
|
||||
|
||||
static VARLINK_DEFINE_ERROR(
|
||||
InvalidParameter,
|
||||
VARLINK_DEFINE_FIELD(parameter, VARLINK_STRING, 0));
|
||||
|
||||
static VARLINK_DEFINE_ERROR(PermissionDenied);
|
||||
|
||||
/* As per https://varlink.org/Service */
|
||||
VARLINK_DEFINE_INTERFACE(
|
||||
org_varlink_service,
|
||||
"org.varlink.service",
|
||||
&vl_method_GetInfo,
|
||||
&vl_method_GetInterfaceDescription,
|
||||
&vl_error_InterfaceNotFound,
|
||||
&vl_error_MethodNotFound,
|
||||
&vl_error_MethodNotImplemented,
|
||||
&vl_error_InvalidParameter,
|
||||
&vl_error_PermissionDenied);
|
||||
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "varlink-idl.h"
|
||||
|
||||
extern const VarlinkInterface vl_interface_org_varlink_service;
|
||||
+515
-30
@@ -3,6 +3,8 @@
|
||||
#include <malloc.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <sd-daemon.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
@@ -10,6 +12,7 @@
|
||||
#include "hashmap.h"
|
||||
#include "io-util.h"
|
||||
#include "list.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "selinux-util.h"
|
||||
#include "serialize.h"
|
||||
@@ -23,6 +26,9 @@
|
||||
#include "user-util.h"
|
||||
#include "varlink.h"
|
||||
#include "varlink-internal.h"
|
||||
#include "varlink-org.varlink.service.h"
|
||||
#include "varlink-io.systemd.h"
|
||||
#include "version.h"
|
||||
|
||||
#define VARLINK_DEFAULT_CONNECTIONS_MAX 4096U
|
||||
#define VARLINK_DEFAULT_CONNECTIONS_PER_UID_MAX 1024U
|
||||
@@ -162,6 +168,7 @@ struct Varlink {
|
||||
VarlinkReply reply_callback;
|
||||
|
||||
JsonVariant *current;
|
||||
VarlinkSymbol *current_method;
|
||||
|
||||
struct ucred ucred;
|
||||
bool ucred_acquired:1;
|
||||
@@ -189,6 +196,8 @@ struct Varlink {
|
||||
sd_event_source *time_event_source;
|
||||
sd_event_source *quit_event_source;
|
||||
sd_event_source *defer_event_source;
|
||||
|
||||
pid_t exec_pid;
|
||||
};
|
||||
|
||||
typedef struct VarlinkServerSocket VarlinkServerSocket;
|
||||
@@ -210,7 +219,9 @@ struct VarlinkServer {
|
||||
|
||||
LIST_HEAD(VarlinkServerSocket, sockets);
|
||||
|
||||
Hashmap *methods;
|
||||
Hashmap *methods; /* Fully qualified symbol name of a method → VarlinkMethod */
|
||||
Hashmap *interfaces; /* Fully qualified interface name → VarlinkInterface* */
|
||||
Hashmap *symbols; /* Fully qualified symbol name of methord/error → VarlinkSymbol* */
|
||||
VarlinkConnect connect_callback;
|
||||
VarlinkDisconnect disconnect_callback;
|
||||
|
||||
@@ -218,13 +229,15 @@ struct VarlinkServer {
|
||||
int64_t event_priority;
|
||||
|
||||
unsigned n_connections;
|
||||
Hashmap *by_uid;
|
||||
Hashmap *by_uid; /* UID_TO_PTR(uid) → UINT_TO_PTR(n_connections) */
|
||||
|
||||
void *userdata;
|
||||
char *description;
|
||||
|
||||
unsigned connections_max;
|
||||
unsigned connections_per_uid_max;
|
||||
|
||||
bool exit_on_idle;
|
||||
};
|
||||
|
||||
static const char* const varlink_state_table[_VARLINK_STATE_MAX] = {
|
||||
@@ -264,6 +277,7 @@ DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(varlink_state, VarlinkState);
|
||||
log_debug("%s: " fmt, varlink_server_description(s), ##__VA_ARGS__)
|
||||
|
||||
static int varlink_format_queue(Varlink *v);
|
||||
static void varlink_server_test_exit_on_idle(VarlinkServer *s);
|
||||
|
||||
static const char *varlink_description(Varlink *v) {
|
||||
return (v ? v->description : NULL) ?: "varlink";
|
||||
@@ -398,6 +412,143 @@ int varlink_connect_address(Varlink **ret, const char *address) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int varlink_connect_exec(Varlink **ret, const char *_command, char **_argv) {
|
||||
_cleanup_close_pair_ int pair[2] = PIPE_EBADF;
|
||||
_cleanup_(sigkill_waitp) pid_t pid = 0;
|
||||
_cleanup_free_ char *command = NULL;
|
||||
_cleanup_strv_free_ char **argv = NULL;
|
||||
int r;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(_command, -EINVAL);
|
||||
|
||||
/* Copy the strings, in case they point into our own argv[], which we'll invalidate shortly because
|
||||
* we rename the child process */
|
||||
command = strdup(_command);
|
||||
if (!command)
|
||||
return -ENOMEM;
|
||||
|
||||
if (strv_isempty(_argv))
|
||||
argv = strv_new(command);
|
||||
else
|
||||
argv = strv_copy(_argv);
|
||||
if (!argv)
|
||||
return -ENOMEM;
|
||||
|
||||
log_debug("Forking off Varlink child process '%s'.", command);
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, pair) < 0)
|
||||
return log_debug_errno(errno, "Failed to allocate AF_UNIX socket pair: %m");
|
||||
|
||||
r = safe_fork_full(
|
||||
"(sd-vlexec)",
|
||||
/* stdio_fds= */ NULL,
|
||||
/* except_fds= */ (int[]) { pair[1] },
|
||||
/* n_except_fds= */ 1,
|
||||
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REOPEN_LOG|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE,
|
||||
&pid);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to spawn process: %m");
|
||||
if (r == 0) {
|
||||
char spid[DECIMAL_STR_MAX(pid_t)+1];
|
||||
const char *setenv_list[] = {
|
||||
"LISTEN_FDS", "1",
|
||||
"LISTEN_PID", spid,
|
||||
"LISTEN_FDNAMES", "varlink",
|
||||
NULL, NULL,
|
||||
};
|
||||
/* Child */
|
||||
|
||||
pair[0] = -EBADF;
|
||||
|
||||
r = move_fd(pair[1], 3, /* cloexec= */ false);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to move file descriptor to 3: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
xsprintf(spid, PID_FMT, pid);
|
||||
|
||||
STRV_FOREACH_PAIR(a, b, setenv_list) {
|
||||
if (setenv(*a, *b, /* override= */ true) < 0) {
|
||||
log_debug_errno(errno, "Failed to set environment variable '%s': %m", *a);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
execvp(command, argv);
|
||||
log_debug_errno(r, "Failed to invoke process '%s': %m", command);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pair[1] = safe_close(pair[1]);
|
||||
|
||||
Varlink *v;
|
||||
r = varlink_new(&v);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create varlink object: %m");
|
||||
|
||||
v->fd = TAKE_FD(pair[0]);
|
||||
v->af = AF_UNIX;
|
||||
v->exec_pid = TAKE_PID(pid);
|
||||
varlink_set_state(v, VARLINK_IDLE_CLIENT);
|
||||
|
||||
*ret = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int varlink_connect_url(Varlink **ret, const char *url) {
|
||||
_cleanup_free_ char *c = NULL;
|
||||
const char *p;
|
||||
bool exec;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(url, -EINVAL);
|
||||
|
||||
// FIXME: Add support for vsock:, ssh-exec:, ssh-unix: URL schemes here. (The latter with OpenSSH
|
||||
// 9.4's -W switch for referencing remote AF_UNIX sockets.)
|
||||
|
||||
/* The Varlink URL scheme is a bit underdefined. We support only the unix: transport for now, plus an
|
||||
* exec: transport we made up ourselves. Strictly speaking this shouldn't even be called URL, since
|
||||
* it has nothing to do with Internet URLs by RFC. */
|
||||
|
||||
p = startswith(url, "unix:");
|
||||
if (p)
|
||||
exec = false;
|
||||
else {
|
||||
p = startswith(url, "exec:");
|
||||
if (!p)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "URL scheme not supported.");
|
||||
|
||||
exec = true;
|
||||
}
|
||||
|
||||
/* The varlink.org reference C library supports more than just file system paths. We might want to
|
||||
* support that one day too. For now simply refuse that. */
|
||||
if (p[strcspn(p, ";?#")] != '\0')
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "URL parameterization with ';', '?', '#' not supported.");
|
||||
|
||||
if (exec || p[0] != '@') { /* no validity checks for abstract namespace */
|
||||
|
||||
if (!path_is_absolute(p))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path not absolute, refusing.");
|
||||
|
||||
c = strdup(p);
|
||||
if (!c)
|
||||
return log_oom_debug();
|
||||
|
||||
path_simplify(c);
|
||||
|
||||
if (!path_is_normalized(c))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path is not normalized, refusing.");
|
||||
}
|
||||
|
||||
if (exec)
|
||||
return varlink_connect_exec(ret, c ?: p, NULL);
|
||||
|
||||
return varlink_connect_address(ret, c ?: p);
|
||||
}
|
||||
|
||||
int varlink_connect_fd(Varlink **ret, int fd) {
|
||||
Varlink *v;
|
||||
int r;
|
||||
@@ -442,6 +593,7 @@ static void varlink_clear_current(Varlink *v) {
|
||||
|
||||
/* Clears the currently processed incoming message */
|
||||
v->current = json_variant_unref(v->current);
|
||||
v->current_method = NULL;
|
||||
|
||||
close_many(v->input_fds, v->n_input_fds);
|
||||
v->input_fds = mfree(v->input_fds);
|
||||
@@ -475,6 +627,11 @@ static void varlink_clear(Varlink *v) {
|
||||
v->output_queue_tail = NULL;
|
||||
|
||||
v->event = sd_event_unref(v->event);
|
||||
|
||||
if (v->exec_pid > 0) {
|
||||
sigterm_wait(v->exec_pid);
|
||||
v->exec_pid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static Varlink* varlink_destroy(Varlink *v) {
|
||||
@@ -987,10 +1144,84 @@ invalid:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int generic_method_get_info(
|
||||
Varlink *link,
|
||||
JsonVariant *parameters,
|
||||
VarlinkMethodFlags flags,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_strv_free_ char **interfaces = NULL;
|
||||
_cleanup_free_ char *product = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (json_variant_elements(parameters) != 0)
|
||||
return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER,
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_VARIANT("parameter", json_variant_by_index(parameters, 0))));
|
||||
|
||||
product = strjoin("systemd (", program_invocation_short_name, ")");
|
||||
if (!product)
|
||||
return -ENOMEM;
|
||||
|
||||
VarlinkInterface *interface;
|
||||
HASHMAP_FOREACH(interface, ASSERT_PTR(link->server)->interfaces) {
|
||||
r = strv_extend(&interfaces, interface->name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
strv_sort(interfaces);
|
||||
|
||||
return varlink_replyb(link, JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_STRING("vendor", "The systemd Project"),
|
||||
JSON_BUILD_PAIR_STRING("product", product),
|
||||
JSON_BUILD_PAIR_STRING("version", STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")"),
|
||||
JSON_BUILD_PAIR_STRING("url", "https://systemd.io/"),
|
||||
JSON_BUILD_PAIR_STRV("interfaces", interfaces)));
|
||||
}
|
||||
|
||||
static int generic_method_get_interface_description(
|
||||
Varlink *link,
|
||||
JsonVariant *parameters,
|
||||
VarlinkMethodFlags flags,
|
||||
void *userdata) {
|
||||
|
||||
static const struct JsonDispatch dispatch_table[] = {
|
||||
{ "interface", JSON_VARIANT_STRING, json_dispatch_const_string, 0, JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
_cleanup_free_ char *text = NULL;
|
||||
const VarlinkInterface *interface;
|
||||
const char *name = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
r = json_dispatch(parameters, dispatch_table, NULL, 0, &name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
interface = hashmap_get(ASSERT_PTR(link->server)->interfaces, name);
|
||||
if (!interface)
|
||||
return varlink_errorb(link, VARLINK_ERROR_INTERFACE_NOT_FOUND,
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_STRING("interface", name)));
|
||||
|
||||
r = varlink_idl_format(interface, &text);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return varlink_replyb(link,
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_STRING("description", text)));
|
||||
}
|
||||
|
||||
static int varlink_dispatch_method(Varlink *v) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *parameters = NULL;
|
||||
VarlinkMethodFlags flags = 0;
|
||||
const char *method = NULL, *error;
|
||||
const char *method = NULL;
|
||||
JsonVariant *e;
|
||||
VarlinkMethod callback;
|
||||
const char *k;
|
||||
@@ -1063,37 +1294,51 @@ static int varlink_dispatch_method(Varlink *v) {
|
||||
|
||||
assert(v->server);
|
||||
|
||||
if (STR_IN_SET(method, "org.varlink.service.GetInfo", "org.varlink.service.GetInterface")) {
|
||||
/* For now, we don't implement a single of varlink's own methods */
|
||||
callback = NULL;
|
||||
error = VARLINK_ERROR_METHOD_NOT_IMPLEMENTED;
|
||||
} else if (startswith(method, "org.varlink.service.")) {
|
||||
callback = NULL;
|
||||
error = VARLINK_ERROR_METHOD_NOT_FOUND;
|
||||
} else {
|
||||
callback = hashmap_get(v->server->methods, method);
|
||||
error = VARLINK_ERROR_METHOD_NOT_FOUND;
|
||||
/* First consult user supplied method implementations */
|
||||
callback = hashmap_get(v->server->methods, method);
|
||||
if (!callback) {
|
||||
if (streq(method, "org.varlink.service.GetInfo"))
|
||||
callback = generic_method_get_info;
|
||||
else if (streq(method, "org.varlink.service.GetInterfaceDescription"))
|
||||
callback = generic_method_get_interface_description;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
r = callback(v, parameters, flags, v->userdata);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Callback for %s returned error: %m", method);
|
||||
bool invalid = false;
|
||||
|
||||
/* We got an error back from the callback. Propagate it to the client if the method call remains unanswered. */
|
||||
if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
|
||||
r = varlink_error_errno(v, r);
|
||||
if (r < 0)
|
||||
return r;
|
||||
v->current_method = hashmap_get(v->server->symbols, method);
|
||||
if (!v->current_method)
|
||||
log_debug("No interface description defined for method '%s', not validating.", method);
|
||||
else {
|
||||
const char *bad_field;
|
||||
|
||||
r = varlink_idl_validate_method_call(v->current_method, parameters, &bad_field);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Parameters for method %s() didn't pass validation on field '%s': %m", method, strna(bad_field));
|
||||
r = varlink_errorb(v, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", bad_field)));
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!invalid) {
|
||||
r = callback(v, parameters, flags, v->userdata);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Callback for %s returned error: %m", method);
|
||||
|
||||
/* We got an error back from the callback. Propagate it to the client if the method call remains unanswered. */
|
||||
if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
|
||||
r = varlink_error_errno(v, r);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
|
||||
assert(error);
|
||||
|
||||
r = varlink_errorb(v, error, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("method", JSON_BUILD_STRING(method))));
|
||||
r = varlink_errorb(v, VARLINK_ERROR_METHOD_NOT_FOUND, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("method", JSON_BUILD_STRING(method))));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
switch (v->state) {
|
||||
|
||||
@@ -1113,7 +1358,6 @@ static int varlink_dispatch_method(Varlink *v) {
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
|
||||
}
|
||||
|
||||
return r;
|
||||
@@ -1282,6 +1526,15 @@ int varlink_wait(Varlink *v, usec_t timeout) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int varlink_is_idle(Varlink *v) {
|
||||
assert_return(v, -EINVAL);
|
||||
|
||||
/* Returns true if there's nothing pending on the connection anymore, i.e. we processed all incoming
|
||||
* or outgoing messages fully, or finished disconnection */
|
||||
|
||||
return IN_SET(v->state, VARLINK_DISCONNECTED, VARLINK_IDLE_CLIENT, VARLINK_IDLE_SERVER);
|
||||
}
|
||||
|
||||
int varlink_get_fd(Varlink *v) {
|
||||
|
||||
assert_return(v, -EINVAL);
|
||||
@@ -1406,6 +1659,7 @@ static void varlink_detach_server(Varlink *v) {
|
||||
if (saved_server->disconnect_callback)
|
||||
saved_server->disconnect_callback(saved_server, v, saved_server->userdata);
|
||||
|
||||
varlink_server_test_exit_on_idle(saved_server);
|
||||
varlink_server_unref(saved_server);
|
||||
varlink_unref(v);
|
||||
}
|
||||
@@ -1849,6 +2103,14 @@ int varlink_reply(Varlink *v, JsonVariant *parameters) {
|
||||
if (r < 0)
|
||||
return varlink_log_errno(v, r, "Failed to build json message: %m");
|
||||
|
||||
if (v->current_method) {
|
||||
const char *bad_field = NULL;
|
||||
|
||||
r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
|
||||
}
|
||||
|
||||
r = varlink_enqueue_json(v, m);
|
||||
if (r < 0)
|
||||
return varlink_log_errno(v, r, "Failed to enqueue json message: %m");
|
||||
@@ -1915,6 +2177,17 @@ int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters) {
|
||||
if (r < 0)
|
||||
return varlink_log_errno(v, r, "Failed to build json message: %m");
|
||||
|
||||
VarlinkSymbol *symbol = hashmap_get(v->server->symbols, error_id);
|
||||
if (!symbol)
|
||||
log_debug("No interface description defined for error '%s', not validating.", error_id);
|
||||
else {
|
||||
const char *bad_field = NULL;
|
||||
|
||||
r = varlink_idl_validate_method_reply(symbol, parameters, &bad_field);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Parameters for error %s didn't pass validation on field '%s', ignoring: %m", error_id, strna(bad_field));
|
||||
}
|
||||
|
||||
r = varlink_enqueue_json(v, m);
|
||||
if (r < 0)
|
||||
return varlink_log_errno(v, r, "Failed to enqueue json message: %m");
|
||||
@@ -2016,6 +2289,14 @@ int varlink_notify(Varlink *v, JsonVariant *parameters) {
|
||||
if (r < 0)
|
||||
return varlink_log_errno(v, r, "Failed to build json message: %m");
|
||||
|
||||
if (v->current_method) {
|
||||
const char *bad_field = NULL;
|
||||
|
||||
r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
|
||||
}
|
||||
|
||||
r = varlink_enqueue_json(v, m);
|
||||
if (r < 0)
|
||||
return varlink_log_errno(v, r, "Failed to enqueue json message: %m");
|
||||
@@ -2445,7 +2726,8 @@ int varlink_set_allow_fd_passing_output(Varlink *v, bool b) {
|
||||
}
|
||||
|
||||
int varlink_server_new(VarlinkServer **ret, VarlinkServerFlags flags) {
|
||||
VarlinkServer *s;
|
||||
_cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
|
||||
int r;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return((flags & ~_VARLINK_SERVER_FLAGS_ALL) == 0, -EINVAL);
|
||||
@@ -2461,7 +2743,14 @@ int varlink_server_new(VarlinkServer **ret, VarlinkServerFlags flags) {
|
||||
.connections_per_uid_max = varlink_server_connections_per_uid_max(NULL),
|
||||
};
|
||||
|
||||
*ret = s;
|
||||
r = varlink_server_add_interface_many(
|
||||
s,
|
||||
&vl_interface_io_systemd,
|
||||
&vl_interface_org_varlink_service);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2477,6 +2766,8 @@ static VarlinkServer* varlink_server_destroy(VarlinkServer *s) {
|
||||
free(m);
|
||||
|
||||
hashmap_free(s->methods);
|
||||
hashmap_free(s->interfaces);
|
||||
hashmap_free(s->symbols);
|
||||
hashmap_free(s->by_uid);
|
||||
|
||||
sd_event_unref(s->event);
|
||||
@@ -2772,6 +3063,49 @@ int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
int varlink_server_listen_auto(VarlinkServer *s) {
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
int r, n = 0;
|
||||
|
||||
assert_return(s, -EINVAL);
|
||||
|
||||
/* Adds all passed fds marked as "varlink" to our varlink server. These fds can either refer to a
|
||||
* listening socket or to a connection socket.
|
||||
*
|
||||
* See https://varlink.org/#activation for the environment variables this is backed by and the
|
||||
* recommended "varlink" identifier in $LISTEN_FDNAMES. */
|
||||
|
||||
r = sd_listen_fds_with_names(/* unset_environment= */ false, &names);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (int i = 0; i < r; i++) {
|
||||
int b, fd;
|
||||
socklen_t l = sizeof(b);
|
||||
|
||||
if (!streq(names[i], "varlink"))
|
||||
continue;
|
||||
|
||||
fd = SD_LISTEN_FDS_START + i;
|
||||
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &b, &l) < 0)
|
||||
return -errno;
|
||||
|
||||
assert(l == sizeof(b));
|
||||
|
||||
if (b) /* Listening socket? */
|
||||
r = varlink_server_listen_fd(s, fd);
|
||||
else /* Otherwise assume connection socket */
|
||||
r = varlink_server_add_connection(s, fd, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void* varlink_server_set_userdata(VarlinkServer *s, void *userdata) {
|
||||
void *ret;
|
||||
|
||||
@@ -2789,6 +3123,34 @@ void* varlink_server_get_userdata(VarlinkServer *s) {
|
||||
return s->userdata;
|
||||
}
|
||||
|
||||
int varlink_server_loop_auto(VarlinkServer *server) {
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
int r;
|
||||
|
||||
assert_return(server, -EINVAL);
|
||||
assert_return(!server->event, -EBUSY);
|
||||
|
||||
/* Runs a Varlink service event loop populated with a passed fd. Exits on the last connection. */
|
||||
|
||||
r = sd_event_new(&event);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = varlink_server_set_exit_on_idle(server, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = varlink_server_attach_event(server, event, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = varlink_server_listen_auto(server);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_event_loop(event);
|
||||
}
|
||||
|
||||
static VarlinkServerSocket* varlink_server_socket_destroy(VarlinkServerSocket *ss) {
|
||||
if (!ss)
|
||||
return NULL;
|
||||
@@ -2813,9 +3175,23 @@ int varlink_server_shutdown(VarlinkServer *s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void varlink_server_test_exit_on_idle(VarlinkServer *s) {
|
||||
assert(s);
|
||||
|
||||
if (s->exit_on_idle && s->event && s->n_connections == 0)
|
||||
(void) sd_event_exit(s->event, 0);
|
||||
}
|
||||
|
||||
int varlink_server_set_exit_on_idle(VarlinkServer *s, bool b) {
|
||||
assert_return(s, -EINVAL);
|
||||
|
||||
s->exit_on_idle = b;
|
||||
varlink_server_test_exit_on_idle(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int varlink_server_add_socket_event_source(VarlinkServer *s, VarlinkServerSocket *ss, int64_t priority) {
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *es = NULL;
|
||||
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
@@ -2880,6 +3256,22 @@ sd_event *varlink_server_get_event(VarlinkServer *s) {
|
||||
return s->event;
|
||||
}
|
||||
|
||||
static bool varlink_symbol_in_interface(const char *method, const char *interface) {
|
||||
const char *p;
|
||||
|
||||
assert(method);
|
||||
assert(interface);
|
||||
|
||||
p = startswith(method, interface);
|
||||
if (!p)
|
||||
return false;
|
||||
|
||||
if (*p != '.')
|
||||
return false;
|
||||
|
||||
return !strchr(p+1, '.');
|
||||
}
|
||||
|
||||
int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMethod callback) {
|
||||
_cleanup_free_ char *m = NULL;
|
||||
int r;
|
||||
@@ -2888,7 +3280,8 @@ int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMeth
|
||||
assert_return(method, -EINVAL);
|
||||
assert_return(callback, -EINVAL);
|
||||
|
||||
if (startswith(method, "org.varlink.service."))
|
||||
if (varlink_symbol_in_interface(method, "org.varlink.service") ||
|
||||
varlink_symbol_in_interface(method, "io.systemd"))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EEXIST), "Cannot bind server to '%s'.", method);
|
||||
|
||||
m = strdup(method);
|
||||
@@ -2952,6 +3345,63 @@ int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect callback)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int varlink_server_add_interface(VarlinkServer *s, const VarlinkInterface *interface) {
|
||||
int r;
|
||||
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(interface, -EINVAL);
|
||||
assert_return(interface->name, -EINVAL);
|
||||
|
||||
if (hashmap_contains(s->interfaces, interface->name))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate registration of interface '%s'.", interface->name);
|
||||
|
||||
r = hashmap_ensure_put(&s->interfaces, &string_hash_ops, interface->name, (void*) interface);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (const VarlinkSymbol *const*symbol = interface->symbols; *symbol; symbol++) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
|
||||
/* We only ever want to validate method calls/replies and errors against the interface
|
||||
* definitions, hence don't bother with the type symbols */
|
||||
if (!IN_SET((*symbol)->symbol_type, VARLINK_METHOD, VARLINK_ERROR))
|
||||
continue;
|
||||
|
||||
j = strjoin(interface->name, ".", (*symbol)->name);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
r = hashmap_ensure_put(&s->symbols, &string_hash_ops_free, j, (void*) *symbol);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
TAKE_PTR(j);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int varlink_server_add_interface_many_internal(VarlinkServer *s, ...) {
|
||||
va_list ap;
|
||||
int r = 0;
|
||||
|
||||
assert_return(s, -EINVAL);
|
||||
|
||||
va_start(ap, s);
|
||||
for (;;) {
|
||||
const VarlinkInterface *interface = va_arg(ap, const VarlinkInterface*);
|
||||
if (!interface)
|
||||
break;
|
||||
|
||||
r = varlink_server_add_interface(s, interface);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned varlink_server_connections_max(VarlinkServer *s) {
|
||||
int dts;
|
||||
|
||||
@@ -3090,3 +3540,38 @@ int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *f
|
||||
LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int varlink_invocation(VarlinkInvocationFlags flags) {
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
int r, b;
|
||||
socklen_t l = sizeof(b);
|
||||
|
||||
/* Returns true if this is a "pure" varlink server invocation, i.e. with one fd passed. */
|
||||
|
||||
r = sd_listen_fds_with_names(/* unset_environment= */ false, &names);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return false;
|
||||
if (r > 1)
|
||||
return -ETOOMANYREFS;
|
||||
|
||||
if (!strv_equal(names, STRV_MAKE("varlink")))
|
||||
return false;
|
||||
|
||||
if (FLAGS_SET(flags, VARLINK_ALLOW_LISTEN|VARLINK_ALLOW_ACCEPT)) /* Both flags set? Then allow everything */
|
||||
return true;
|
||||
|
||||
if ((flags & (VARLINK_ALLOW_LISTEN|VARLINK_ALLOW_ACCEPT)) == 0) /* Neither is set, then fail */
|
||||
return -EISCONN;
|
||||
|
||||
if (getsockopt(SD_LISTEN_FDS_START, SOL_SOCKET, SO_ACCEPTCONN, &b, &l) < 0)
|
||||
return -errno;
|
||||
|
||||
assert(l == sizeof(b));
|
||||
|
||||
if (!FLAGS_SET(flags, b ? VARLINK_ALLOW_LISTEN : VARLINK_ALLOW_ACCEPT))
|
||||
return -EISCONN;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+26
-2
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "json.h"
|
||||
#include "time-util.h"
|
||||
#include "varlink-idl.h"
|
||||
|
||||
/* A minimal Varlink implementation. We only implement the minimal, obvious bits here though. No validation,
|
||||
* no introspection, no name service, just the stuff actually needed.
|
||||
@@ -45,7 +46,6 @@ typedef enum VarlinkServerFlags {
|
||||
VARLINK_SERVER_MYSELF_ONLY = 1 << 1, /* Only accessible by our own UID */
|
||||
VARLINK_SERVER_ACCOUNT_UID = 1 << 2, /* Do per user accounting */
|
||||
VARLINK_SERVER_INHERIT_USERDATA = 1 << 3, /* Initialize Varlink connection userdata from VarlinkServer userdata */
|
||||
|
||||
_VARLINK_SERVER_FLAGS_ALL = (1 << 4) - 1,
|
||||
} VarlinkServerFlags;
|
||||
|
||||
@@ -55,6 +55,8 @@ typedef int (*VarlinkConnect)(VarlinkServer *server, Varlink *link, void *userda
|
||||
typedef void (*VarlinkDisconnect)(VarlinkServer *server, Varlink *link, void *userdata);
|
||||
|
||||
int varlink_connect_address(Varlink **ret, const char *address);
|
||||
int varlink_connect_exec(Varlink **ret, const char *command, char **argv);
|
||||
int varlink_connect_url(Varlink **ret, const char *url);
|
||||
int varlink_connect_fd(Varlink **ret, int fd);
|
||||
|
||||
Varlink* varlink_ref(Varlink *link);
|
||||
@@ -71,6 +73,8 @@ sd_event *varlink_get_event(Varlink *v);
|
||||
int varlink_process(Varlink *v);
|
||||
int varlink_wait(Varlink *v, usec_t timeout);
|
||||
|
||||
int varlink_is_idle(Varlink *v);
|
||||
|
||||
int varlink_flush(Varlink *v);
|
||||
int varlink_close(Varlink *v);
|
||||
|
||||
@@ -142,6 +146,7 @@ VarlinkServer *varlink_server_unref(VarlinkServer *s);
|
||||
/* Add addresses or fds to listen on */
|
||||
int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t mode);
|
||||
int varlink_server_listen_fd(VarlinkServer *s, int fd);
|
||||
int varlink_server_listen_auto(VarlinkServer *s);
|
||||
int varlink_server_add_connection(VarlinkServer *s, int fd, Varlink **ret);
|
||||
|
||||
/* Bind callbacks */
|
||||
@@ -151,6 +156,11 @@ int varlink_server_bind_method_many_internal(VarlinkServer *s, ...);
|
||||
int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect connect);
|
||||
int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect disconnect);
|
||||
|
||||
/* Add interface definition */
|
||||
int varlink_server_add_interface(VarlinkServer *s, const VarlinkInterface *interface);
|
||||
int varlink_server_add_interface_many_internal(VarlinkServer *s, ...);
|
||||
#define varlink_server_add_interface_many(s, ...) varlink_server_add_interface_many_internal(s, __VA_ARGS__, NULL)
|
||||
|
||||
void* varlink_server_set_userdata(VarlinkServer *s, void *userdata);
|
||||
void* varlink_server_get_userdata(VarlinkServer *s);
|
||||
|
||||
@@ -158,8 +168,12 @@ int varlink_server_attach_event(VarlinkServer *v, sd_event *e, int64_t priority)
|
||||
int varlink_server_detach_event(VarlinkServer *v);
|
||||
sd_event *varlink_server_get_event(VarlinkServer *v);
|
||||
|
||||
int varlink_server_loop_auto(VarlinkServer *server);
|
||||
|
||||
int varlink_server_shutdown(VarlinkServer *server);
|
||||
|
||||
int varlink_server_set_exit_on_idle(VarlinkServer *s, bool b);
|
||||
|
||||
unsigned varlink_server_connections_max(VarlinkServer *s);
|
||||
unsigned varlink_server_connections_per_uid_max(VarlinkServer *s);
|
||||
|
||||
@@ -170,6 +184,15 @@ unsigned varlink_server_current_connections(VarlinkServer *s);
|
||||
|
||||
int varlink_server_set_description(VarlinkServer *s, const char *description);
|
||||
|
||||
typedef enum VarlinkInvocationFlags {
|
||||
VARLINK_ALLOW_LISTEN = 1 << 0,
|
||||
VARLINK_ALLOW_ACCEPT = 1 << 1,
|
||||
_VARLINK_SERVER_INVOCATION_FLAGS_MAX = (1 << 2) - 1,
|
||||
_VARLINK_SERVER_INVOCATION_FLAGS_INVALID = -EINVAL,
|
||||
} VarlinkInvocationFlags;
|
||||
|
||||
int varlink_invocation(VarlinkInvocationFlags flags);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_unref);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_close_unref);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_flush_close_unref);
|
||||
@@ -179,6 +202,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServer *, varlink_server_unref);
|
||||
#define VARLINK_ERROR_DISCONNECTED "io.systemd.Disconnected"
|
||||
#define VARLINK_ERROR_TIMEOUT "io.systemd.TimedOut"
|
||||
#define VARLINK_ERROR_PROTOCOL "io.systemd.Protocol"
|
||||
|
||||
/* This one we invented, and use for generically propagating system errors (errno) to clients */
|
||||
#define VARLINK_ERROR_SYSTEM "io.systemd.System"
|
||||
|
||||
/* These are errors defined in the Varlink spec */
|
||||
@@ -188,5 +213,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServer *, varlink_server_unref);
|
||||
#define VARLINK_ERROR_INVALID_PARAMETER "org.varlink.service.InvalidParameter"
|
||||
|
||||
/* These are errors we came up with and squatted the namespace with */
|
||||
#define VARLINK_ERROR_SUBSCRIPTION_TAKEN "org.varlink.service.SubscriptionTaken"
|
||||
#define VARLINK_ERROR_PERMISSION_DENIED "org.varlink.service.PermissionDenied"
|
||||
|
||||
@@ -417,6 +417,10 @@ executables += [
|
||||
'sources' : files('test-varlink.c'),
|
||||
'dependencies' : threads,
|
||||
},
|
||||
test_template + {
|
||||
'sources' : files('test-varlink-idl.c'),
|
||||
'dependencies' : threads,
|
||||
},
|
||||
test_template + {
|
||||
'sources' : files('test-watchdog.c'),
|
||||
'type' : 'unsafe',
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "fd-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "tests.h"
|
||||
#include "varlink.h"
|
||||
#include "varlink-idl.h"
|
||||
#include "varlink-io.systemd.h"
|
||||
#include "varlink-io.systemd.Journal.h"
|
||||
#include "varlink-io.systemd.ManagedOOM.h"
|
||||
#include "varlink-io.systemd.PCRExtend.h"
|
||||
#include "varlink-io.systemd.Resolve.Monitor.h"
|
||||
#include "varlink-io.systemd.Resolve.h"
|
||||
#include "varlink-io.systemd.UserDatabase.h"
|
||||
#include "varlink-io.systemd.oom.h"
|
||||
#include "varlink-org.varlink.service.h"
|
||||
|
||||
static VARLINK_DEFINE_ENUM_TYPE(
|
||||
EnumTest,
|
||||
VARLINK_DEFINE_ENUM_VALUE(foo),
|
||||
VARLINK_DEFINE_ENUM_VALUE(bar),
|
||||
VARLINK_DEFINE_ENUM_VALUE(baz));
|
||||
|
||||
static VARLINK_DEFINE_STRUCT_TYPE(
|
||||
NestedStructTest,
|
||||
VARLINK_DEFINE_FIELD(x, VARLINK_INT, 0));
|
||||
|
||||
static VARLINK_DEFINE_STRUCT_TYPE(
|
||||
StructTest,
|
||||
|
||||
VARLINK_DEFINE_FIELD(bbb, VARLINK_BOOL, 0),
|
||||
VARLINK_DEFINE_FIELD(bbbn, VARLINK_BOOL, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(bbba, VARLINK_BOOL, VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(bbbna, VARLINK_BOOL, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(bbbm, VARLINK_BOOL, VARLINK_MAP),
|
||||
VARLINK_DEFINE_FIELD(bbbnm, VARLINK_BOOL, VARLINK_NULLABLE|VARLINK_MAP),
|
||||
|
||||
VARLINK_DEFINE_FIELD(iii, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_FIELD(iiin, VARLINK_INT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(iiia, VARLINK_INT, VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(iiina, VARLINK_INT, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(iiim, VARLINK_INT, VARLINK_MAP),
|
||||
VARLINK_DEFINE_FIELD(iiinm, VARLINK_INT, VARLINK_NULLABLE|VARLINK_MAP),
|
||||
|
||||
VARLINK_DEFINE_FIELD(fff, VARLINK_FLOAT, 0),
|
||||
VARLINK_DEFINE_FIELD(fffn, VARLINK_FLOAT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(fffa, VARLINK_FLOAT, VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(fffna, VARLINK_FLOAT, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(fffm, VARLINK_FLOAT, VARLINK_MAP),
|
||||
VARLINK_DEFINE_FIELD(fffnm, VARLINK_FLOAT, VARLINK_NULLABLE|VARLINK_MAP),
|
||||
|
||||
VARLINK_DEFINE_FIELD(sss, VARLINK_STRING, 0),
|
||||
VARLINK_DEFINE_FIELD(sssn, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(sssa, VARLINK_STRING, VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(sssna, VARLINK_STRING, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(sssm, VARLINK_STRING, VARLINK_MAP),
|
||||
VARLINK_DEFINE_FIELD(sssnm, VARLINK_STRING, VARLINK_NULLABLE|VARLINK_MAP),
|
||||
|
||||
VARLINK_DEFINE_FIELD(ooo, VARLINK_OBJECT, 0),
|
||||
VARLINK_DEFINE_FIELD(ooon, VARLINK_OBJECT, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD(oooa, VARLINK_OBJECT, VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(ooona, VARLINK_OBJECT, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD(ooom, VARLINK_OBJECT, VARLINK_MAP),
|
||||
VARLINK_DEFINE_FIELD(ooonm, VARLINK_OBJECT, VARLINK_NULLABLE|VARLINK_MAP),
|
||||
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(eee, EnumTest, 0),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(eeen, EnumTest, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(eeea, EnumTest, VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(eeena, EnumTest, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(eeem, EnumTest, VARLINK_MAP),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(eeenm, EnumTest, VARLINK_NULLABLE|VARLINK_MAP),
|
||||
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(nnn, NestedStructTest, 0),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(nnnn, NestedStructTest, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(nnna, NestedStructTest, VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(nnnna, NestedStructTest, VARLINK_NULLABLE|VARLINK_ARRAY),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(nnnm, NestedStructTest, VARLINK_MAP),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(nnnnm, NestedStructTest, VARLINK_NULLABLE|VARLINK_MAP));
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
MethodTest,
|
||||
VARLINK_DEFINE_INPUT(x, VARLINK_BOOL, 0),
|
||||
VARLINK_DEFINE_INPUT_BY_TYPE(y, EnumTest, 0),
|
||||
VARLINK_DEFINE_INPUT_BY_TYPE(z, StructTest, 0),
|
||||
VARLINK_DEFINE_OUTPUT(x, VARLINK_BOOL, 0),
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(y, EnumTest, 0),
|
||||
VARLINK_DEFINE_OUTPUT_BY_TYPE(z, StructTest, 0));
|
||||
|
||||
static VARLINK_DEFINE_ERROR(
|
||||
ErrorTest,
|
||||
VARLINK_DEFINE_FIELD(x, VARLINK_BOOL, 0),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(y, EnumTest, 0),
|
||||
VARLINK_DEFINE_FIELD_BY_TYPE(z, StructTest, 0));
|
||||
|
||||
static VARLINK_DEFINE_INTERFACE(
|
||||
xyz_test,
|
||||
"xyz.test",
|
||||
&vl_type_EnumTest,
|
||||
&vl_type_NestedStructTest,
|
||||
&vl_type_StructTest,
|
||||
&vl_method_MethodTest,
|
||||
&vl_error_ErrorTest);
|
||||
|
||||
static void test_parse_format_one(const VarlinkInterface *iface) {
|
||||
_cleanup_(varlink_interface_freep) VarlinkInterface *parsed = NULL;
|
||||
_cleanup_free_ char *text = NULL, *text2 = NULL;
|
||||
|
||||
assert_se(iface);
|
||||
|
||||
assert_se(varlink_idl_dump(stdout, /* use_colors=*/ true, iface) >= 0);
|
||||
assert_se(varlink_idl_consistent(iface, LOG_ERR) >= 0);
|
||||
assert_se(varlink_idl_format(iface, &text) >= 0);
|
||||
assert_se(varlink_idl_parse(text, NULL, NULL, &parsed) >= 0);
|
||||
assert_se(varlink_idl_consistent(parsed, LOG_ERR) >= 0);
|
||||
assert_se(varlink_idl_format(parsed, &text2) >= 0);
|
||||
assert_se(streq(text, text2));
|
||||
}
|
||||
|
||||
TEST(parse_format) {
|
||||
test_parse_format_one(&vl_interface_org_varlink_service);
|
||||
print_separator();
|
||||
test_parse_format_one(&vl_interface_io_systemd_UserDatabase);
|
||||
print_separator();
|
||||
test_parse_format_one(&vl_interface_io_systemd_Journal);
|
||||
print_separator();
|
||||
test_parse_format_one(&vl_interface_io_systemd_Resolve);
|
||||
print_separator();
|
||||
test_parse_format_one(&vl_interface_io_systemd_Resolve_Monitor);
|
||||
print_separator();
|
||||
test_parse_format_one(&vl_interface_io_systemd_ManagedOOM);
|
||||
print_separator();
|
||||
test_parse_format_one(&vl_interface_io_systemd_oom);
|
||||
print_separator();
|
||||
test_parse_format_one(&vl_interface_io_systemd);
|
||||
print_separator();
|
||||
test_parse_format_one(&vl_interface_io_systemd_PCRExtend);
|
||||
print_separator();
|
||||
test_parse_format_one(&vl_interface_xyz_test);
|
||||
}
|
||||
|
||||
TEST(parse) {
|
||||
_cleanup_(varlink_interface_freep) VarlinkInterface *parsed = NULL;
|
||||
|
||||
/* This one has (nested) enonymous enums and structs */
|
||||
static const char text[] =
|
||||
"interface quu.waa\n"
|
||||
"type Fooenum ( a, b, c )\n"
|
||||
"type Barstruct ( a : (x, y, z), b : (x : int), c: (f, ff, fff), d: object, e : (sub : (subsub: (subsubsub: string, subsubsub2: (iii, ooo)))))"
|
||||
;
|
||||
|
||||
assert_se(varlink_idl_parse(text, NULL, NULL, &parsed) >= 0);
|
||||
test_parse_format_one(parsed);
|
||||
}
|
||||
|
||||
TEST(interface_name_is_valid) {
|
||||
assert_se(!varlink_idl_interface_name_is_valid(NULL));
|
||||
assert_se(!varlink_idl_interface_name_is_valid(""));
|
||||
assert_se(!varlink_idl_interface_name_is_valid(","));
|
||||
assert_se(!varlink_idl_interface_name_is_valid("."));
|
||||
assert_se(!varlink_idl_interface_name_is_valid("-"));
|
||||
assert_se(varlink_idl_interface_name_is_valid("a"));
|
||||
assert_se(varlink_idl_interface_name_is_valid("a.a"));
|
||||
assert_se(!varlink_idl_interface_name_is_valid("-.a"));
|
||||
assert_se(!varlink_idl_interface_name_is_valid("-a.a"));
|
||||
assert_se(!varlink_idl_interface_name_is_valid("a-.a"));
|
||||
assert_se(varlink_idl_interface_name_is_valid("a-a.a"));
|
||||
assert_se(!varlink_idl_interface_name_is_valid("a-a.a-"));
|
||||
assert_se(!varlink_idl_interface_name_is_valid("a-a.-a"));
|
||||
assert_se(!varlink_idl_interface_name_is_valid("a-a.-"));
|
||||
assert_se(varlink_idl_interface_name_is_valid("a-a.a-a"));
|
||||
assert_se(varlink_idl_interface_name_is_valid("io.systemd.Foobar"));
|
||||
}
|
||||
|
||||
TEST(symbol_name_is_valid) {
|
||||
assert_se(!varlink_idl_symbol_name_is_valid(NULL));
|
||||
assert_se(!varlink_idl_symbol_name_is_valid(""));
|
||||
assert_se(!varlink_idl_symbol_name_is_valid("_"));
|
||||
assert_se(!varlink_idl_symbol_name_is_valid("_foo"));
|
||||
assert_se(varlink_idl_symbol_name_is_valid("Foofoo"));
|
||||
assert_se(varlink_idl_symbol_name_is_valid("Foo"));
|
||||
assert_se(varlink_idl_symbol_name_is_valid("Foo0"));
|
||||
assert_se(!varlink_idl_symbol_name_is_valid("0Foo"));
|
||||
assert_se(!varlink_idl_symbol_name_is_valid("foo"));
|
||||
assert_se(varlink_idl_symbol_name_is_valid("Foo0foo"));
|
||||
assert_se(!varlink_idl_symbol_name_is_valid("bool"));
|
||||
assert_se(!varlink_idl_symbol_name_is_valid("int"));
|
||||
assert_se(!varlink_idl_symbol_name_is_valid("float"));
|
||||
assert_se(!varlink_idl_symbol_name_is_valid("string"));
|
||||
assert_se(!varlink_idl_symbol_name_is_valid("object"));
|
||||
}
|
||||
|
||||
TEST(field_name_is_valid) {
|
||||
assert_se(!varlink_idl_field_name_is_valid(NULL));
|
||||
assert_se(!varlink_idl_field_name_is_valid(""));
|
||||
assert_se(!varlink_idl_field_name_is_valid("_"));
|
||||
assert_se(!varlink_idl_field_name_is_valid("_foo"));
|
||||
assert_se(!varlink_idl_field_name_is_valid("_foo_"));
|
||||
assert_se(!varlink_idl_field_name_is_valid("foo_"));
|
||||
assert_se(varlink_idl_field_name_is_valid("foo_foo"));
|
||||
assert_se(varlink_idl_field_name_is_valid("f_o_o_f_o_o"));
|
||||
assert_se(!varlink_idl_field_name_is_valid("foo__foo"));
|
||||
assert_se(varlink_idl_field_name_is_valid("Foofoo"));
|
||||
assert_se(varlink_idl_field_name_is_valid("Foo"));
|
||||
assert_se(varlink_idl_field_name_is_valid("Foo0"));
|
||||
assert_se(!varlink_idl_field_name_is_valid("0Foo"));
|
||||
assert_se(varlink_idl_field_name_is_valid("foo"));
|
||||
assert_se(varlink_idl_field_name_is_valid("Foo0foo"));
|
||||
assert_se(varlink_idl_field_name_is_valid("foo0foo"));
|
||||
}
|
||||
|
||||
TEST(validate_json) {
|
||||
|
||||
_cleanup_(varlink_interface_freep) VarlinkInterface *parsed = NULL;
|
||||
|
||||
/* This one has (nested) enonymous enums and structs */
|
||||
static const char text[] =
|
||||
"interface validate.test\n"
|
||||
"method Mymethod ( a:string, b:int, c:?bool, d:[]int, e:?[string]bool, f:?(piff, paff), g:(f:float) ) -> ()\n";
|
||||
|
||||
assert_se(varlink_idl_parse(text, NULL, NULL, &parsed) >= 0);
|
||||
test_parse_format_one(parsed);
|
||||
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
|
||||
assert_se(json_build(&v, JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR("a", JSON_BUILD_STRING("x")),
|
||||
JSON_BUILD_PAIR("b", JSON_BUILD_UNSIGNED(44)),
|
||||
JSON_BUILD_PAIR("d", JSON_BUILD_ARRAY(JSON_BUILD_UNSIGNED(5), JSON_BUILD_UNSIGNED(7), JSON_BUILD_UNSIGNED(107))),
|
||||
JSON_BUILD_PAIR("g", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("f", JSON_BUILD_REAL(0.5f)))))) >= 0);
|
||||
|
||||
json_variant_dump(v, JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO, stdout, NULL);
|
||||
|
||||
const VarlinkSymbol* symbol = ASSERT_PTR(varlink_idl_find_symbol(parsed, VARLINK_METHOD, "Mymethod"));
|
||||
|
||||
assert_se(varlink_idl_validate_method_call(symbol, v, NULL) >= 0);
|
||||
}
|
||||
|
||||
static int test_method(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
||||
JsonVariant *foo = json_variant_by_key(parameters, "foo"), *bar = json_variant_by_key(parameters, "bar");
|
||||
|
||||
return varlink_replyb(link,
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_UNSIGNED("waldo", json_variant_unsigned(foo) * json_variant_unsigned(bar)),
|
||||
JSON_BUILD_PAIR_UNSIGNED("quux", json_variant_unsigned(foo) + json_variant_unsigned(bar))));
|
||||
}
|
||||
|
||||
static int done_method(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
||||
assert_se(sd_event_exit(varlink_get_event(link), 0) >= 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static VARLINK_DEFINE_METHOD(
|
||||
TestMethod,
|
||||
VARLINK_DEFINE_INPUT(foo, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_INPUT(bar, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_INPUT(optional, VARLINK_STRING, VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_OUTPUT(waldo, VARLINK_INT, 0),
|
||||
VARLINK_DEFINE_OUTPUT(quux, VARLINK_INT, 0));
|
||||
|
||||
static VARLINK_DEFINE_METHOD(Done);
|
||||
|
||||
static VARLINK_DEFINE_INTERFACE(
|
||||
xyz,
|
||||
"xyz",
|
||||
&vl_method_TestMethod,
|
||||
&vl_method_Done);
|
||||
|
||||
|
||||
static void* server_thread(void *userdata) {
|
||||
_cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
|
||||
assert_se(varlink_server_new(&server, 0) >= 0);
|
||||
assert_se(varlink_server_add_interface(server, &vl_interface_xyz) >= 0);
|
||||
assert_se(varlink_server_bind_method(server, "xyz.TestMethod", test_method) >= 0);
|
||||
assert_se(varlink_server_bind_method(server, "xyz.Done", done_method) >= 0);
|
||||
|
||||
assert_se(sd_event_new(&event) >= 0);
|
||||
assert_se(varlink_server_attach_event(server, event, 0) >= 0);
|
||||
|
||||
assert_se(varlink_server_add_connection(server, PTR_TO_FD(userdata), NULL) >= 0);
|
||||
|
||||
assert_se(sd_event_loop(event) >= 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TEST(validate_method_call) {
|
||||
_cleanup_close_pair_ int fd[2] = PIPE_EBADF;
|
||||
_cleanup_(varlink_unrefp) Varlink *v = NULL;
|
||||
pthread_t t;
|
||||
|
||||
assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, fd) >= 0);
|
||||
assert_se(pthread_create(&t, NULL, server_thread, FD_TO_PTR(TAKE_FD(fd[1]))) == 0);
|
||||
assert_se(varlink_connect_fd(&v, TAKE_FD(fd[0])) >= 0);
|
||||
|
||||
JsonVariant *reply = NULL;
|
||||
const char *error_id = NULL;
|
||||
assert_se(varlink_callb(v, "xyz.TestMethod", &reply, &error_id, NULL,
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_UNSIGNED("foo", 8),
|
||||
JSON_BUILD_PAIR_UNSIGNED("bar", 9))) >= 0);
|
||||
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *expected_reply = NULL;
|
||||
assert_se(json_build(&expected_reply,
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_UNSIGNED("waldo", 8*9),
|
||||
JSON_BUILD_PAIR_UNSIGNED("quux", 8+9))) >= 0);
|
||||
|
||||
assert_se(!error_id);
|
||||
|
||||
json_variant_dump(reply, JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO, NULL, NULL);
|
||||
json_variant_dump(expected_reply, JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO, NULL, NULL);
|
||||
assert_se(json_variant_equal(reply, expected_reply));
|
||||
|
||||
assert_se(varlink_callb(v, "xyz.TestMethod", &reply, &error_id, NULL,
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_UNSIGNED("foo", 9),
|
||||
JSON_BUILD_PAIR_UNSIGNED("bar", 8),
|
||||
JSON_BUILD_PAIR_STRING("optional", "pfft"))) >= 0);
|
||||
|
||||
assert_se(!error_id);
|
||||
assert_se(json_variant_equal(reply, expected_reply));
|
||||
|
||||
assert_se(varlink_callb(v, "xyz.TestMethod", &reply, &error_id, NULL,
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_UNSIGNED("foo", 8),
|
||||
JSON_BUILD_PAIR_UNSIGNED("bar", 9),
|
||||
JSON_BUILD_PAIR_STRING("zzz", "pfft"))) >= 0);
|
||||
assert_se(streq_ptr(error_id, VARLINK_ERROR_INVALID_PARAMETER));
|
||||
|
||||
assert_se(varlink_callb(v, "xyz.TestMethod", &reply, &error_id, NULL,
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_STRING("foo", "wuff"),
|
||||
JSON_BUILD_PAIR_UNSIGNED("bar", 9))) >= 0);
|
||||
assert_se(streq_ptr(error_id, VARLINK_ERROR_INVALID_PARAMETER));
|
||||
|
||||
assert_se(varlink_send(v, "xyz.Done", NULL) >= 0);
|
||||
assert_se(varlink_flush(v) >= 0);
|
||||
assert_se(pthread_join(t, NULL) == 0);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "user-util.h"
|
||||
#include "userdb.h"
|
||||
#include "varlink.h"
|
||||
#include "varlink-io.systemd.UserDatabase.h"
|
||||
|
||||
#define ITERATIONS_MAX 64U
|
||||
#define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
|
||||
@@ -483,6 +484,10 @@ static int run(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate server: %m");
|
||||
|
||||
r = varlink_server_add_interface(server, &vl_interface_io_systemd_UserDatabase);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
|
||||
|
||||
r = varlink_server_bind_method_many(
|
||||
server,
|
||||
"io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
varlinkctl_sources = files(
|
||||
'varlinkctl.c',
|
||||
)
|
||||
|
||||
executables += [
|
||||
executable_template + {
|
||||
'name' : 'varlinkctl',
|
||||
'public' : true,
|
||||
'sources' : varlinkctl_sources,
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,520 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include "build.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "main-func.h"
|
||||
#include "pager.h"
|
||||
#include "parse-argument.h"
|
||||
#include "path-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "terminal-util.h"
|
||||
#include "varlink.h"
|
||||
#include "verbs.h"
|
||||
#include "version.h"
|
||||
|
||||
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
|
||||
static PagerFlags arg_pager_flags = 0;
|
||||
static VarlinkMethodFlags arg_method_flags = 0;
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("varlinkctl", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
printf("%1$s [OPTIONS...] COMMAND ...\n\n"
|
||||
"%5$sIntrospect Varlink Services.%6$s\n"
|
||||
"\n%3$sCommands:%4$s\n"
|
||||
" info ADDRESS Show service information\n"
|
||||
" list-interfaces ADDRESS\n"
|
||||
" List interfaces implemented by service\n"
|
||||
" introspect ADDRESS INTERFACE\n"
|
||||
" Show interface definition\n"
|
||||
" call ADDRESS METHOD [PARAMS]\n"
|
||||
" Invoke method\n"
|
||||
" validate-idl [FILE] Validate interface description\n"
|
||||
" help Show this help\n"
|
||||
"\n%3$sOptions:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
" --more Request multiple responses\n"
|
||||
" --oneway Do not request response\n"
|
||||
" --json=MODE Output as JSON\n"
|
||||
" -j Same as --json=pretty on tty, --json=short otherwise\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
ansi_highlight(),
|
||||
ansi_normal());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_help(int argc, char **argv, void *userdata) {
|
||||
return help();
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_NO_PAGER,
|
||||
ARG_MORE,
|
||||
ARG_ONEWAY,
|
||||
ARG_JSON,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "more", no_argument, NULL, ARG_MORE },
|
||||
{ "oneway", no_argument, NULL, ARG_ONEWAY },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{},
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hj", options, NULL)) >= 0)
|
||||
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
return help();
|
||||
|
||||
case ARG_VERSION:
|
||||
return version();
|
||||
|
||||
case ARG_NO_PAGER:
|
||||
arg_pager_flags |= PAGER_DISABLE;
|
||||
break;
|
||||
|
||||
case ARG_MORE:
|
||||
arg_method_flags = (arg_method_flags & ~VARLINK_METHOD_ONEWAY) | VARLINK_METHOD_MORE;
|
||||
break;
|
||||
|
||||
case ARG_ONEWAY:
|
||||
arg_method_flags = (arg_method_flags & ~VARLINK_METHOD_MORE) | VARLINK_METHOD_ONEWAY;
|
||||
break;
|
||||
|
||||
case ARG_JSON:
|
||||
r = parse_json_argument(optarg, &arg_json_format_flags);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case 'j':
|
||||
arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
/* If more than one reply is expected, imply JSON-SEQ output */
|
||||
if (FLAGS_SET(arg_method_flags, VARLINK_METHOD_MORE))
|
||||
arg_json_format_flags |= JSON_FORMAT_SEQ;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int varlink_connect_auto(Varlink **ret, const char *where) {
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(where);
|
||||
|
||||
if (STARTSWITH_SET(where, "/", "./")) { /* If the string starts with a slash or dot slash we use it as a file system path */
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
struct stat st;
|
||||
|
||||
fd = open(where, O_PATH|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "Failed to open '%s': %m", where);
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat '%s': %m", where);
|
||||
|
||||
/* Is this a socket in the fs? Then connect() to it. */
|
||||
if (S_ISSOCK(st.st_mode)) {
|
||||
r = varlink_connect_address(ret, FORMAT_PROC_FD_PATH(fd));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to connect to '%s': %m", where);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Is this an executable binary? Then fork it off. */
|
||||
if (S_ISREG(st.st_mode) && (st.st_mode & 0111)) {
|
||||
r = varlink_connect_exec(ret, where, STRV_MAKE(where)); /* Ideally we'd use FORMAT_PROC_FD_PATH(fd) here too, but that breaks the #! logic */
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to spawn '%s' process: %m", where);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unrecognized path '%s' is neither an AF_UNIX socket, nor an executable binary.", where);
|
||||
}
|
||||
|
||||
/* Otherwise assume this is an URL */
|
||||
r = varlink_connect_url(ret, where);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to connect to URL '%s': %m", where);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct GetInfoData {
|
||||
const char *vendor;
|
||||
const char *product;
|
||||
const char *version;
|
||||
const char *url;
|
||||
char **interfaces;
|
||||
} GetInfoData;
|
||||
|
||||
static void get_info_data_done(GetInfoData *d) {
|
||||
assert(d);
|
||||
|
||||
d->interfaces = strv_free(d->interfaces);
|
||||
}
|
||||
|
||||
static int verb_info(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(varlink_unrefp) Varlink *vl = NULL;
|
||||
const char *url;
|
||||
int r;
|
||||
|
||||
assert(argc == 2);
|
||||
url = argv[1];
|
||||
|
||||
r = varlink_connect_auto(&vl, url);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
JsonVariant *reply = NULL;
|
||||
const char *error = NULL;
|
||||
r = varlink_call(vl, "org.varlink.service.GetInfo", NULL, &reply, &error, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to issue GetInfo() call: %m");
|
||||
if (error)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call GetInfo() failed: %s", error);
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
|
||||
static const struct JsonDispatch dispatch_table[] = {
|
||||
{ "vendor", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(GetInfoData, vendor), JSON_MANDATORY },
|
||||
{ "product", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(GetInfoData, product), JSON_MANDATORY },
|
||||
{ "version", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(GetInfoData, version), JSON_MANDATORY },
|
||||
{ "url", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(GetInfoData, url), JSON_MANDATORY },
|
||||
{ "interfaces", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(GetInfoData, interfaces), JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
_cleanup_(get_info_data_done) GetInfoData data = {};
|
||||
|
||||
r = json_dispatch(reply, dispatch_table, NULL, JSON_LOG, &data);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
strv_sort(data.interfaces);
|
||||
|
||||
if (streq_ptr(argv[0], "list-interfaces")) {
|
||||
STRV_FOREACH(i, data.interfaces)
|
||||
puts(*i);
|
||||
} else {
|
||||
_cleanup_(table_unrefp) Table *t = NULL;
|
||||
|
||||
t = table_new_vertical();
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(
|
||||
t,
|
||||
TABLE_FIELD, "Vendor",
|
||||
TABLE_STRING, data.vendor,
|
||||
TABLE_FIELD, "Product",
|
||||
TABLE_STRING, data.product,
|
||||
TABLE_FIELD, "Version",
|
||||
TABLE_STRING, data.version,
|
||||
TABLE_FIELD, "URL",
|
||||
TABLE_STRING, data.url,
|
||||
TABLE_SET_URL, data.url,
|
||||
TABLE_FIELD, "Interfaces",
|
||||
TABLE_STRV, data.interfaces);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
r = table_print(t, NULL);
|
||||
if (r < 0)
|
||||
return table_log_print_error(r);
|
||||
}
|
||||
} else {
|
||||
JsonVariant *v;
|
||||
|
||||
v = streq_ptr(argv[0], "list-interfaces") ?
|
||||
json_variant_by_key(reply, "interfaces") : reply;
|
||||
|
||||
json_variant_dump(v, arg_json_format_flags, stdout, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct GetInterfaceDescriptionData {
|
||||
const char *description;
|
||||
} GetInterfaceDescriptionData;
|
||||
|
||||
static int verb_introspect(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(varlink_unrefp) Varlink *vl = NULL;
|
||||
const char *url, *interface;
|
||||
int r;
|
||||
|
||||
assert(argc == 3);
|
||||
url = argv[1];
|
||||
interface = argv[2];
|
||||
|
||||
r = varlink_connect_auto(&vl, url);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
JsonVariant *reply = NULL;
|
||||
const char *error = NULL;
|
||||
r = varlink_callb(vl, "org.varlink.service.GetInterfaceDescription", &reply, &error, NULL, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("interface", interface)));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to issue GetInterfaceDescription() call: %m");
|
||||
if (error)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call GetInterfaceDescription() failed: %s", error);
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
|
||||
static const struct JsonDispatch dispatch_table[] = {
|
||||
{ "description", JSON_VARIANT_STRING, json_dispatch_const_string, 0, JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
_cleanup_(varlink_interface_freep) VarlinkInterface *vi = NULL;
|
||||
const char *description = NULL;
|
||||
unsigned line = 0, column = 0;
|
||||
|
||||
r = json_dispatch(reply, dispatch_table, NULL, JSON_LOG, &description);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Try to parse the returned description, so that we can add syntax highlighting */
|
||||
r = varlink_idl_parse(ASSERT_PTR(description), &line, &column, &vi);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to parse returned interface description at %u:%u, showing raw interface description: %m", line, column);
|
||||
|
||||
fputs(description, stdout);
|
||||
if (!endswith(description, "\n"))
|
||||
fputs("\n", stdout);
|
||||
} else {
|
||||
r = varlink_idl_dump(stdout, /* use_colors= */ -1, vi);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to format parsed interface description: %m");
|
||||
}
|
||||
} else
|
||||
json_variant_dump(reply, arg_json_format_flags, stdout, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reply_callback(
|
||||
Varlink *link,
|
||||
JsonVariant *parameters,
|
||||
const char *error,
|
||||
VarlinkReplyFlags flags,
|
||||
void *userdata) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (error) {
|
||||
/* Propagate the error we received via sd_notify() */
|
||||
(void) sd_notifyf(/* unset_environment= */ false, "VARLINKERROR=%s", error);
|
||||
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call failed: %s", error);
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
json_variant_dump(parameters, arg_json_format_flags, stdout, NULL);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int verb_call(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *jp = NULL;
|
||||
_cleanup_(varlink_unrefp) Varlink *vl = NULL;
|
||||
const char *url, *method, *parameter;
|
||||
unsigned line = 0, column = 0;
|
||||
int r;
|
||||
|
||||
assert(argc >= 3);
|
||||
assert(argc <= 4);
|
||||
url = argv[1];
|
||||
method = argv[2];
|
||||
parameter = argc > 3 && !streq(argv[3], "-") ? argv[3] : NULL;
|
||||
|
||||
arg_json_format_flags &= ~JSON_FORMAT_OFF;
|
||||
|
||||
if (parameter) {
|
||||
r = json_parse_with_source(parameter, "<argv[4]>", 0, &jp, &line, &column);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse parameters at <argv[4]>:%u:%u: %m", line, column);
|
||||
} else {
|
||||
r = json_parse_file_at(stdin, AT_FDCWD, "<stdin>", 0, &jp, &line, &column);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse parameters at <stdin>:%u:%u: %m", line, column);
|
||||
}
|
||||
|
||||
r = varlink_connect_auto(&vl, url);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_method_flags & VARLINK_METHOD_ONEWAY) {
|
||||
r = varlink_send(vl, method, jp);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to issue %s() call: %m", method);
|
||||
|
||||
r = varlink_flush(vl);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to flush Varlink connection: %m");
|
||||
|
||||
} else if (arg_method_flags & VARLINK_METHOD_MORE) {
|
||||
|
||||
varlink_set_userdata(vl, (void*) method);
|
||||
|
||||
r = varlink_bind_reply(vl, reply_callback);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind reply callback: %m");
|
||||
|
||||
r = varlink_observe(vl, method, jp);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to issue %s() call: %m", method);
|
||||
|
||||
for (;;) {
|
||||
r = varlink_is_idle(vl);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check if varlink connection is idle: %m");
|
||||
if (r > 0)
|
||||
break;
|
||||
|
||||
r = varlink_process(vl);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to process varlink connection: %m");
|
||||
if (r != 0)
|
||||
continue;
|
||||
|
||||
r = varlink_wait(vl, USEC_INFINITY);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to wait for varlink connection events: %m");
|
||||
}
|
||||
} else {
|
||||
JsonVariant *reply = NULL;
|
||||
const char *error = NULL;
|
||||
|
||||
r = varlink_call(vl, method, jp, &reply, &error, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to issue %s() call: %m", method);
|
||||
|
||||
/* If the server returned an error to us, then fail, but first output the associated parameters */
|
||||
if (error) {
|
||||
/* Propagate the error we received via sd_notify() */
|
||||
(void) sd_notifyf(/* unset_environment= */ false, "VARLINKERROR=%s", error);
|
||||
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call %s() failed: %s", method, error);
|
||||
} else
|
||||
r = 0;
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
json_variant_dump(reply, arg_json_format_flags, stdout, NULL);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_validate_idl(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(varlink_interface_freep) VarlinkInterface *vi = NULL;
|
||||
_cleanup_free_ char *text = NULL;
|
||||
const char *fname;
|
||||
unsigned line = 1, column = 1;
|
||||
int r;
|
||||
|
||||
fname = argc > 1 ? argv[1] : NULL;
|
||||
|
||||
if (fname) {
|
||||
r = read_full_file(fname, &text, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read interface description file '%s': %m", fname);
|
||||
} else {
|
||||
r = read_full_stream(stdin, &text, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read interface description from stdin: %m");
|
||||
|
||||
fname = "<stdin>";
|
||||
}
|
||||
|
||||
r = varlink_idl_parse(text, &line, &column, &vi);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "%s:%u:%u: Failed to parse interface description: %m", fname, line, column);
|
||||
|
||||
r = varlink_idl_consistent(vi, LOG_ERR);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
r = varlink_idl_dump(stdout, /* use_colors= */ -1, vi);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to format parsed interface description: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int varlinkctl_main(int argc, char *argv[]) {
|
||||
static const Verb verbs[] = {
|
||||
{ "info", 2, 2, 0, verb_info },
|
||||
{ "list-interfaces", 2, 2, 0, verb_info },
|
||||
{ "introspect", 3, 3, 0, verb_introspect },
|
||||
{ "call", 3, 4, 0, verb_call },
|
||||
{ "validate-idl", 1, 2, 0, verb_validate_idl },
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, verb_help },
|
||||
{}
|
||||
};
|
||||
|
||||
return dispatch_verb(argc, argv, verbs, NULL);
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return varlinkctl_main(argc, argv);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
@@ -10,8 +10,12 @@ for ((i = 0; i < ITERATIONS; i++)); do
|
||||
dd if=/dev/urandom bs=1M count=1 | base64 | systemd-cat
|
||||
done
|
||||
journalctl --rotate
|
||||
# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --rotate' via varlinkctl
|
||||
varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Rotate '{}'
|
||||
journalctl --flush
|
||||
varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.FlushToVar '{}'
|
||||
journalctl --sync
|
||||
varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}'
|
||||
journalctl --rotate --vacuum-size=8M
|
||||
|
||||
# Reset the ratelimit buckets for the subsequent tests below.
|
||||
@@ -42,7 +46,8 @@ write_and_match "<5> \t Leading spaces\n" " \t Leading spaces\n" --level-prefix
|
||||
# --output-fields restricts output
|
||||
ID="$(systemd-id128 new)"
|
||||
echo -ne "foo" | systemd-cat -t "$ID" --level-prefix false
|
||||
journalctl --sync
|
||||
# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --sync' via varlinkctl
|
||||
varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}'
|
||||
journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/tmp/output
|
||||
[[ $(wc -l </tmp/output) -eq 9 ]]
|
||||
grep -q '^__CURSOR=' /tmp/output
|
||||
|
||||
@@ -285,6 +285,14 @@ userdbctl groups-of-user 🐱
|
||||
userdbctl services
|
||||
userdbctl services -j | jq
|
||||
|
||||
varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"testuser","service":"io.systemd.Multiplexer"}'
|
||||
varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"root","service":"io.systemd.Multiplexer"}'
|
||||
varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"dropinuser","service":"io.systemd.Multiplexer"}'
|
||||
varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"uid":2000000,"service":"io.systemd.Multiplexer"}'
|
||||
(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"","service":"io.systemd.Multiplexer"}')
|
||||
(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"🐱","service":"io.systemd.Multiplexer"}')
|
||||
(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"i-do-not-exist","service":"io.systemd.Multiplexer"}')
|
||||
|
||||
userdbctl ssh-authorized-keys dropinuser | tee /tmp/authorized-keys
|
||||
grep "ssh-ed25519" /tmp/authorized-keys
|
||||
grep "ecdsa-sha2-nistp256" /tmp/authorized-keys
|
||||
|
||||
@@ -270,7 +270,16 @@ if [[ -x "$SD_PCREXTEND" ]] && tpm_has_pcr sha256 11 && tpm_has_pcr sha256 15; t
|
||||
|
||||
# And similar for the boot phase measurement into PCR 11
|
||||
tpm2_pcrread sha256:11 -Q -o /tmp/oldpcr11
|
||||
SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" foobar
|
||||
# Do the equivalent of 'SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" foobar' via Varlink, just to test the Varlink logic (but first we need to patch out the conditionalization...)
|
||||
mkdir -p /run/systemd/system/systemd-pcrextend.socket.d
|
||||
cat > /run/systemd/system/systemd-pcrextend.socket.d/50-no-condition.conf <<EOF
|
||||
[Unit]
|
||||
# Turn off all conditions */
|
||||
ConditionSecurity=
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl restart systemd-pcrextend.socket
|
||||
varlinkctl call /run/systemd/io.systemd.PCRExtend io.systemd.PCRExtend.Extend '{"pcr":11,"text":"foobar"}'
|
||||
tpm2_pcrread sha256:11 -Q -o /tmp/newpcr11
|
||||
|
||||
diff /tmp/newpcr11 \
|
||||
|
||||
@@ -430,6 +430,15 @@ units = [
|
||||
'file' : 'systemd-oomd.socket',
|
||||
'conditions' : ['ENABLE_OOMD'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-pcrextend@.service.in',
|
||||
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-pcrextend.socket',
|
||||
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
|
||||
'symlinks' : ['sockets.target.wants/'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-pcrfs-root.service.in',
|
||||
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=TPM2 PCR Extension (Varlink)
|
||||
Documentation=man:systemd-pcrextend(8)
|
||||
DefaultDependencies=no
|
||||
Before=sockets.target
|
||||
ConditionSecurity=measured-uki
|
||||
|
||||
[Socket]
|
||||
ListenStream=/run/systemd/io.systemd.PCRExtend
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
Accept=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -0,0 +1,19 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=TPM2 PCR Extension (Varlink)
|
||||
Documentation=man:systemd-pcrphase.service(8)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target initrd-switch-root.target
|
||||
Before=shutdown.target initrd-switch-root.target
|
||||
|
||||
[Service]
|
||||
Environment=LISTEN_FDNAMES=varlink
|
||||
ExecStart=-{{LIBEXECDIR}}/systemd-pcrextend
|
||||
Reference in New Issue
Block a user