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:
Yu Watanabe
2023-10-06 21:58:17 +09:00
committed by GitHub
47 changed files with 4259 additions and 87 deletions
+10
View File
@@ -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
+158
View File
@@ -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);
+19
View File
@@ -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);
+6
View File
@@ -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;
+14
View File
@@ -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;
+76
View File
@@ -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);
+6
View File
@@ -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;
+21
View File
@@ -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);
+6
View File
@@ -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;
+25
View File
@@ -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);
+7
View File
@@ -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;
+45
View File
@@ -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);
+6
View File
@@ -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
View File
@@ -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
View File
@@ -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"