Move busctl sources to src/busctl

busctl is not part of libsystemd, and should not be stored under libsystemd.
In particular this is confusing because busctl is linked with libshared, but
stuff in libsystemd is not supposed to depend on libshared.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2017-04-04 20:36:09 -04:00
parent b0b46a2c12
commit 3bcaf458f6
4 changed files with 3 additions and 3 deletions
+782
View File
@@ -0,0 +1,782 @@
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "sd-bus.h"
#include "alloc-util.h"
#include "busctl-introspect.h"
#include "string-util.h"
#include "util.h"
#include "xml.h"
#define NODE_DEPTH_MAX 16
typedef struct Context {
const XMLIntrospectOps *ops;
void *userdata;
char *interface_name;
uint64_t interface_flags;
char *member_name;
char *member_signature;
char *member_result;
uint64_t member_flags;
bool member_writable;
const char *current;
void *xml_state;
} Context;
static void context_reset_member(Context *c) {
free(c->member_name);
free(c->member_signature);
free(c->member_result);
c->member_name = c->member_signature = c->member_result = NULL;
c->member_flags = 0;
c->member_writable = false;
}
static void context_reset_interface(Context *c) {
c->interface_name = mfree(c->interface_name);
c->interface_flags = 0;
context_reset_member(c);
}
static int parse_xml_annotation(Context *context, uint64_t *flags) {
enum {
STATE_ANNOTATION,
STATE_NAME,
STATE_VALUE
} state = STATE_ANNOTATION;
_cleanup_free_ char *field = NULL, *value = NULL;
assert(context);
for (;;) {
_cleanup_free_ char *name = NULL;
int t;
t = xml_tokenize(&context->current, &name, &context->xml_state, NULL);
if (t < 0) {
log_error("XML parse error.");
return t;
}
if (t == XML_END) {
log_error("Premature end of XML data.");
return -EBADMSG;
}
switch (state) {
case STATE_ANNOTATION:
if (t == XML_ATTRIBUTE_NAME) {
if (streq_ptr(name, "name"))
state = STATE_NAME;
else if (streq_ptr(name, "value"))
state = STATE_VALUE;
else {
log_error("Unexpected <annotation> attribute %s.", name);
return -EBADMSG;
}
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "annotation"))) {
if (flags) {
if (streq_ptr(field, "org.freedesktop.DBus.Deprecated")) {
if (streq_ptr(value, "true"))
*flags |= SD_BUS_VTABLE_DEPRECATED;
} else if (streq_ptr(field, "org.freedesktop.DBus.Method.NoReply")) {
if (streq_ptr(value, "true"))
*flags |= SD_BUS_VTABLE_METHOD_NO_REPLY;
} else if (streq_ptr(field, "org.freedesktop.DBus.Property.EmitsChangedSignal")) {
if (streq_ptr(value, "const"))
*flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) | SD_BUS_VTABLE_PROPERTY_CONST;
else if (streq_ptr(value, "invalidates"))
*flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST)) | SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION;
else if (streq_ptr(value, "false"))
*flags = *flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION);
}
}
return 0;
} else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
log_error("Unexpected token in <annotation>. (1)");
return -EINVAL;
}
break;
case STATE_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
free_and_replace(field, name);
state = STATE_ANNOTATION;
} else {
log_error("Unexpected token in <annotation>. (2)");
return -EINVAL;
}
break;
case STATE_VALUE:
if (t == XML_ATTRIBUTE_VALUE) {
free_and_replace(value, name);
state = STATE_ANNOTATION;
} else {
log_error("Unexpected token in <annotation>. (3)");
return -EINVAL;
}
break;
default:
assert_not_reached("Bad state");
}
}
}
static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth) {
enum {
STATE_NODE,
STATE_NODE_NAME,
STATE_INTERFACE,
STATE_INTERFACE_NAME,
STATE_METHOD,
STATE_METHOD_NAME,
STATE_METHOD_ARG,
STATE_METHOD_ARG_NAME,
STATE_METHOD_ARG_TYPE,
STATE_METHOD_ARG_DIRECTION,
STATE_SIGNAL,
STATE_SIGNAL_NAME,
STATE_SIGNAL_ARG,
STATE_SIGNAL_ARG_NAME,
STATE_SIGNAL_ARG_TYPE,
STATE_SIGNAL_ARG_DIRECTION,
STATE_PROPERTY,
STATE_PROPERTY_NAME,
STATE_PROPERTY_TYPE,
STATE_PROPERTY_ACCESS,
} state = STATE_NODE;
_cleanup_free_ char *node_path = NULL, *argument_type = NULL, *argument_direction = NULL;
const char *np = prefix;
int r;
assert(context);
assert(prefix);
if (n_depth > NODE_DEPTH_MAX) {
log_error("<node> depth too high.");
return -EINVAL;
}
for (;;) {
_cleanup_free_ char *name = NULL;
int t;
t = xml_tokenize(&context->current, &name, &context->xml_state, NULL);
if (t < 0) {
log_error("XML parse error.");
return t;
}
if (t == XML_END) {
log_error("Premature end of XML data.");
return -EBADMSG;
}
switch (state) {
case STATE_NODE:
if (t == XML_ATTRIBUTE_NAME) {
if (streq_ptr(name, "name"))
state = STATE_NODE_NAME;
else {
log_error("Unexpected <node> attribute %s.", name);
return -EBADMSG;
}
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "interface"))
state = STATE_INTERFACE;
else if (streq_ptr(name, "node")) {
r = parse_xml_node(context, np, n_depth+1);
if (r < 0)
return r;
} else {
log_error("Unexpected <node> tag %s.", name);
return -EBADMSG;
}
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "node"))) {
if (context->ops->on_path) {
r = context->ops->on_path(node_path ? node_path : np, context->userdata);
if (r < 0)
return r;
}
return 0;
} else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
log_error("Unexpected token in <node>. (1)");
return -EINVAL;
}
break;
case STATE_NODE_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
free(node_path);
if (name[0] == '/') {
node_path = name;
name = NULL;
} else {
if (endswith(prefix, "/"))
node_path = strappend(prefix, name);
else
node_path = strjoin(prefix, "/", name);
if (!node_path)
return log_oom();
}
np = node_path;
state = STATE_NODE;
} else {
log_error("Unexpected token in <node>. (2)");
return -EINVAL;
}
break;
case STATE_INTERFACE:
if (t == XML_ATTRIBUTE_NAME) {
if (streq_ptr(name, "name"))
state = STATE_INTERFACE_NAME;
else {
log_error("Unexpected <interface> attribute %s.", name);
return -EBADMSG;
}
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "method"))
state = STATE_METHOD;
else if (streq_ptr(name, "signal"))
state = STATE_SIGNAL;
else if (streq_ptr(name, "property")) {
context->member_flags |= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE;
state = STATE_PROPERTY;
} else if (streq_ptr(name, "annotation")) {
r = parse_xml_annotation(context, &context->interface_flags);
if (r < 0)
return r;
} else {
log_error("Unexpected <interface> tag %s.", name);
return -EINVAL;
}
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "interface"))) {
if (n_depth == 0) {
if (context->ops->on_interface) {
r = context->ops->on_interface(context->interface_name, context->interface_flags, context->userdata);
if (r < 0)
return r;
}
context_reset_interface(context);
}
state = STATE_NODE;
} else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
log_error("Unexpected token in <interface>. (1)");
return -EINVAL;
}
break;
case STATE_INTERFACE_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
if (n_depth == 0)
free_and_replace(context->interface_name, name);
state = STATE_INTERFACE;
} else {
log_error("Unexpected token in <interface>. (2)");
return -EINVAL;
}
break;
case STATE_METHOD:
if (t == XML_ATTRIBUTE_NAME) {
if (streq_ptr(name, "name"))
state = STATE_METHOD_NAME;
else {
log_error("Unexpected <method> attribute %s", name);
return -EBADMSG;
}
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "arg"))
state = STATE_METHOD_ARG;
else if (streq_ptr(name, "annotation")) {
r = parse_xml_annotation(context, &context->member_flags);
if (r < 0)
return r;
} else {
log_error("Unexpected <method> tag %s.", name);
return -EINVAL;
}
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "method"))) {
if (n_depth == 0) {
if (context->ops->on_method) {
r = context->ops->on_method(context->interface_name, context->member_name, context->member_signature, context->member_result, context->member_flags, context->userdata);
if (r < 0)
return r;
}
context_reset_member(context);
}
state = STATE_INTERFACE;
} else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
log_error("Unexpected token in <method> (1).");
return -EINVAL;
}
break;
case STATE_METHOD_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
if (n_depth == 0)
free_and_replace(context->member_name, name);
state = STATE_METHOD;
} else {
log_error("Unexpected token in <method> (2).");
return -EINVAL;
}
break;
case STATE_METHOD_ARG:
if (t == XML_ATTRIBUTE_NAME) {
if (streq_ptr(name, "name"))
state = STATE_METHOD_ARG_NAME;
else if (streq_ptr(name, "type"))
state = STATE_METHOD_ARG_TYPE;
else if (streq_ptr(name, "direction"))
state = STATE_METHOD_ARG_DIRECTION;
else {
log_error("Unexpected method <arg> attribute %s.", name);
return -EBADMSG;
}
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "annotation")) {
r = parse_xml_annotation(context, NULL);
if (r < 0)
return r;
} else {
log_error("Unexpected method <arg> tag %s.", name);
return -EINVAL;
}
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) {
if (n_depth == 0) {
if (argument_type) {
if (!argument_direction || streq(argument_direction, "in")) {
if (!strextend(&context->member_signature, argument_type, NULL))
return log_oom();
} else if (streq(argument_direction, "out")) {
if (!strextend(&context->member_result, argument_type, NULL))
return log_oom();
} else
log_error("Unexpected method <arg> direction value '%s'.", argument_direction);
}
argument_type = mfree(argument_type);
argument_direction = mfree(argument_direction);
}
state = STATE_METHOD;
} else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
log_error("Unexpected token in method <arg>. (1)");
return -EINVAL;
}
break;
case STATE_METHOD_ARG_NAME:
if (t == XML_ATTRIBUTE_VALUE)
state = STATE_METHOD_ARG;
else {
log_error("Unexpected token in method <arg>. (2)");
return -EINVAL;
}
break;
case STATE_METHOD_ARG_TYPE:
if (t == XML_ATTRIBUTE_VALUE) {
free_and_replace(argument_type, name);
state = STATE_METHOD_ARG;
} else {
log_error("Unexpected token in method <arg>. (3)");
return -EINVAL;
}
break;
case STATE_METHOD_ARG_DIRECTION:
if (t == XML_ATTRIBUTE_VALUE) {
free_and_replace(argument_direction, name);
state = STATE_METHOD_ARG;
} else {
log_error("Unexpected token in method <arg>. (4)");
return -EINVAL;
}
break;
case STATE_SIGNAL:
if (t == XML_ATTRIBUTE_NAME) {
if (streq_ptr(name, "name"))
state = STATE_SIGNAL_NAME;
else {
log_error("Unexpected <signal> attribute %s.", name);
return -EBADMSG;
}
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "arg"))
state = STATE_SIGNAL_ARG;
else if (streq_ptr(name, "annotation")) {
r = parse_xml_annotation(context, &context->member_flags);
if (r < 0)
return r;
} else {
log_error("Unexpected <signal> tag %s.", name);
return -EINVAL;
}
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "signal"))) {
if (n_depth == 0) {
if (context->ops->on_signal) {
r = context->ops->on_signal(context->interface_name, context->member_name, context->member_signature, context->member_flags, context->userdata);
if (r < 0)
return r;
}
context_reset_member(context);
}
state = STATE_INTERFACE;
} else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
log_error("Unexpected token in <signal>. (1)");
return -EINVAL;
}
break;
case STATE_SIGNAL_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
if (n_depth == 0)
free_and_replace(context->member_name, name);
state = STATE_SIGNAL;
} else {
log_error("Unexpected token in <signal>. (2)");
return -EINVAL;
}
break;
case STATE_SIGNAL_ARG:
if (t == XML_ATTRIBUTE_NAME) {
if (streq_ptr(name, "name"))
state = STATE_SIGNAL_ARG_NAME;
else if (streq_ptr(name, "type"))
state = STATE_SIGNAL_ARG_TYPE;
else if (streq_ptr(name, "direction"))
state = STATE_SIGNAL_ARG_DIRECTION;
else {
log_error("Unexpected signal <arg> attribute %s.", name);
return -EBADMSG;
}
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "annotation")) {
r = parse_xml_annotation(context, NULL);
if (r < 0)
return r;
} else {
log_error("Unexpected signal <arg> tag %s.", name);
return -EINVAL;
}
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) {
if (argument_type) {
if (!argument_direction || streq(argument_direction, "out")) {
if (!strextend(&context->member_signature, argument_type, NULL))
return log_oom();
} else
log_error("Unexpected signal <arg> direction value '%s'.", argument_direction);
argument_type = mfree(argument_type);
}
state = STATE_SIGNAL;
} else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
log_error("Unexpected token in signal <arg> (1).");
return -EINVAL;
}
break;
case STATE_SIGNAL_ARG_NAME:
if (t == XML_ATTRIBUTE_VALUE)
state = STATE_SIGNAL_ARG;
else {
log_error("Unexpected token in signal <arg> (2).");
return -EINVAL;
}
break;
case STATE_SIGNAL_ARG_TYPE:
if (t == XML_ATTRIBUTE_VALUE) {
free_and_replace(argument_type, name);
state = STATE_SIGNAL_ARG;
} else {
log_error("Unexpected token in signal <arg> (3).");
return -EINVAL;
}
break;
case STATE_SIGNAL_ARG_DIRECTION:
if (t == XML_ATTRIBUTE_VALUE) {
free_and_replace(argument_direction, name);
state = STATE_SIGNAL_ARG;
} else {
log_error("Unexpected token in signal <arg>. (4)");
return -EINVAL;
}
break;
case STATE_PROPERTY:
if (t == XML_ATTRIBUTE_NAME) {
if (streq_ptr(name, "name"))
state = STATE_PROPERTY_NAME;
else if (streq_ptr(name, "type"))
state = STATE_PROPERTY_TYPE;
else if (streq_ptr(name, "access"))
state = STATE_PROPERTY_ACCESS;
else {
log_error("Unexpected <property> attribute %s.", name);
return -EBADMSG;
}
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "annotation")) {
r = parse_xml_annotation(context, &context->member_flags);
if (r < 0)
return r;
} else {
log_error("Unexpected <property> tag %s.", name);
return -EINVAL;
}
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "property"))) {
if (n_depth == 0) {
if (context->ops->on_property) {
r = context->ops->on_property(context->interface_name, context->member_name, context->member_signature, context->member_writable, context->member_flags, context->userdata);
if (r < 0)
return r;
}
context_reset_member(context);
}
state = STATE_INTERFACE;
} else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
log_error("Unexpected token in <property>. (1)");
return -EINVAL;
}
break;
case STATE_PROPERTY_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
if (n_depth == 0)
free_and_replace(context->member_name, name);
state = STATE_PROPERTY;
} else {
log_error("Unexpected token in <property>. (2)");
return -EINVAL;
}
break;
case STATE_PROPERTY_TYPE:
if (t == XML_ATTRIBUTE_VALUE) {
if (n_depth == 0)
free_and_replace(context->member_signature, name);
state = STATE_PROPERTY;
} else {
log_error("Unexpected token in <property>. (3)");
return -EINVAL;
}
break;
case STATE_PROPERTY_ACCESS:
if (t == XML_ATTRIBUTE_VALUE) {
if (streq(name, "readwrite") || streq(name, "write"))
context->member_writable = true;
state = STATE_PROPERTY;
} else {
log_error("Unexpected token in <property>. (4)");
return -EINVAL;
}
break;
}
}
}
int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata) {
Context context = {
.ops = ops,
.userdata = userdata,
.current = xml,
};
int r;
assert(prefix);
assert(xml);
assert(ops);
for (;;) {
_cleanup_free_ char *name = NULL;
r = xml_tokenize(&context.current, &name, &context.xml_state, NULL);
if (r < 0) {
log_error("XML parse error");
goto finish;
}
if (r == XML_END) {
r = 0;
break;
}
if (r == XML_TAG_OPEN) {
if (streq(name, "node")) {
r = parse_xml_node(&context, prefix, 0);
if (r < 0)
goto finish;
} else {
log_error("Unexpected tag '%s' in introspection data.", name);
r = -EBADMSG;
goto finish;
}
} else if (r != XML_TEXT || !in_charset(name, WHITESPACE)) {
log_error("Unexpected token.");
r = -EBADMSG;
goto finish;
}
}
finish:
context_reset_interface(&context);
return r;
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
typedef struct XMLIntrospectOps {
int (*on_path)(const char *path, void *userdata);
int (*on_interface)(const char *name, uint64_t flags, void *userdata);
int (*on_method)(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata);
int (*on_signal)(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata);
int (*on_property)(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata);
} XMLIntrospectOps;
int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata);
+2086
View File
File diff suppressed because it is too large Load Diff