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:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user