config: add replies and dbus api documentation
Add replies, which use standard X error values, to the two currently-supported input configuration requests. Document the D-BUS API we use. Make sure we free everything when we encounter an error. Add a _source option to all incoming requests, noting that it came from a client. Reject all requests to add a device where an option name contains an underscore.
This commit is contained in:
parent
a05044cfc1
commit
ec35e7198d
286
config/config.c
286
config/config.c
|
@ -29,17 +29,26 @@
|
|||
#define DBUS_API_SUBJECT_TO_CHANGE
|
||||
#include <dbus/dbus.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <X11/X.h>
|
||||
|
||||
#include "opaque.h" /* for 'display': there has to be a better way */
|
||||
/* the above comment lies. there is no better way. */
|
||||
#include "input.h"
|
||||
#include "inputstr.h"
|
||||
#include "config.h"
|
||||
#include "os.h"
|
||||
|
||||
#define MATCH_RULE "type='method_call',interface='org.x.config.input'"
|
||||
|
||||
#define MALFORMED_MSG "config: malformed message, dropping"
|
||||
#define MALFORMED_MESSAGE() DebugF(MALFORMED_MSG)
|
||||
#define MALFORMED_MESSAGE_ERROR() DebugF(MALFORMED_MSG ": %s, %s", \
|
||||
error.name, error.message)
|
||||
#define MALFORMED_MSG "[config] malformed message, dropping"
|
||||
#define MALFORMED_MESSAGE() { DebugF(MALFORMED_MSG "\n"); \
|
||||
ret = BadValue; \
|
||||
goto unwind; }
|
||||
#define MALFORMED_MESSAGE_ERROR() { DebugF(MALFORMED_MSG ": %s, %s", \
|
||||
error->name, error->message); \
|
||||
ret = BadValue; \
|
||||
goto unwind; }
|
||||
|
||||
static DBusConnection *configConnection = NULL;
|
||||
static int configfd = -1;
|
||||
|
@ -55,130 +64,186 @@ configDispatch()
|
|||
dbus_connection_read_write_dispatch(configConnection, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
configAddDevice(DBusMessage *message, DBusMessageIter *iter, DBusError *error)
|
||||
{
|
||||
DBusMessageIter subiter;
|
||||
InputOption *tmpo = NULL, *options = NULL;
|
||||
char *tmp = NULL;
|
||||
int ret = BadMatch;
|
||||
|
||||
DebugF("[config] adding device\n");
|
||||
|
||||
/* signature should be [ss][ss]... */
|
||||
options = (InputOption *) xcalloc(sizeof(InputOption), 1);
|
||||
if (!options) {
|
||||
ErrorF("[config] couldn't allocate option\n");
|
||||
return BadAlloc;
|
||||
}
|
||||
|
||||
options->key = xstrdup("_source");
|
||||
options->value = xstrdup("client/dbus");
|
||||
|
||||
while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) {
|
||||
tmpo = (InputOption *) xcalloc(sizeof(InputOption), 1);
|
||||
if (!tmpo) {
|
||||
ErrorF("[config] couldn't allocate option\n");
|
||||
ret = BadAlloc;
|
||||
goto unwind;
|
||||
}
|
||||
|
||||
dbus_message_iter_recurse(iter, &subiter);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&subiter) != DBUS_TYPE_STRING)
|
||||
MALFORMED_MESSAGE();
|
||||
|
||||
dbus_message_iter_get_basic(&subiter, &tmp);
|
||||
if (!tmp)
|
||||
MALFORMED_MESSAGE();
|
||||
if (tmp[0] == '_') {
|
||||
ErrorF("[config] attempted subterfuge: option name %s given\n",
|
||||
tmp);
|
||||
MALFORMED_MESSAGE();
|
||||
}
|
||||
tmpo->key = xstrdup(tmp);
|
||||
if (!tmpo->key) {
|
||||
ErrorF("[config] couldn't duplicate key!\n");
|
||||
ret = BadAlloc;
|
||||
goto unwind;
|
||||
}
|
||||
|
||||
if (!dbus_message_iter_has_next(&subiter))
|
||||
MALFORMED_MESSAGE();
|
||||
dbus_message_iter_next(&subiter);
|
||||
if (dbus_message_iter_get_arg_type(&subiter) != DBUS_TYPE_STRING)
|
||||
MALFORMED_MESSAGE();
|
||||
|
||||
dbus_message_iter_get_basic(&subiter, &tmp);
|
||||
if (!tmp)
|
||||
MALFORMED_MESSAGE();
|
||||
tmpo->value = xstrdup(tmp);
|
||||
if (!tmpo->value) {
|
||||
ErrorF("[config] couldn't duplicate option!\n");
|
||||
ret = BadAlloc;
|
||||
goto unwind;
|
||||
}
|
||||
|
||||
tmpo->next = options;
|
||||
options = tmpo;
|
||||
dbus_message_iter_next(iter);
|
||||
}
|
||||
|
||||
ret = NewInputDeviceRequest(options);
|
||||
if (ret != Success)
|
||||
DebugF("[config] NewInputDeviceRequest failed\n");
|
||||
|
||||
return ret;
|
||||
|
||||
unwind:
|
||||
if (tmpo->key)
|
||||
xfree(tmpo->key);
|
||||
if (tmpo->value)
|
||||
xfree(tmpo->value);
|
||||
if (tmpo)
|
||||
xfree(tmpo);
|
||||
|
||||
while (options) {
|
||||
tmpo = options;
|
||||
options = options->next;
|
||||
if (tmpo->key)
|
||||
xfree(tmpo->key);
|
||||
if (tmpo->value)
|
||||
xfree(tmpo->value);
|
||||
xfree(tmpo);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
configRemoveDevice(DBusMessage *message, DBusMessageIter *iter,
|
||||
DBusError *error)
|
||||
{
|
||||
int deviceid = -1;
|
||||
int ret = BadMatch;
|
||||
DeviceIntPtr pDev = NULL;
|
||||
|
||||
if (!dbus_message_get_args(message, error, DBUS_TYPE_INT32,
|
||||
&deviceid, DBUS_TYPE_INVALID)) {
|
||||
MALFORMED_MESSAGE_ERROR();
|
||||
}
|
||||
|
||||
if (deviceid < 0 || !(pDev = LookupDeviceIntRec(deviceid))) {
|
||||
DebugF("[config] bogus device id %d given\n", deviceid);
|
||||
ret = BadMatch;
|
||||
goto unwind;
|
||||
}
|
||||
|
||||
DebugF("[config] removing device %s (id %d)\n", pDev->name, deviceid);
|
||||
|
||||
/* Call PIE here so we don't try to dereference a device that's
|
||||
* already been removed. */
|
||||
OsBlockSignals();
|
||||
ProcessInputEvents();
|
||||
RemoveDevice(pDev);
|
||||
OsReleaseSignals();
|
||||
|
||||
return Success;
|
||||
|
||||
unwind:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
configMessage(DBusConnection *connection, DBusMessage *message, void *closure)
|
||||
{
|
||||
InputOption *option = NULL, *ret = NULL;
|
||||
DBusMessageIter iter, subiter;
|
||||
DBusMessageIter iter;
|
||||
DBusError error;
|
||||
char *tmp = NULL;
|
||||
int deviceid = -1;
|
||||
DeviceIntPtr pDev = NULL;
|
||||
DBusMessage *reply;
|
||||
DBusConnection *bus = closure;
|
||||
int ret = BadDrawable; /* nonsensical value */
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
if (strcmp(dbus_message_get_interface(message),
|
||||
"org.x.config.input") == 0) {
|
||||
if (!dbus_message_iter_init(message, &iter)) {
|
||||
ErrorF("config: failed to init iterator\n");
|
||||
ErrorF("[config] failed to init iterator\n");
|
||||
dbus_error_free(&error);
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY; /* ?? */
|
||||
}
|
||||
if (strcmp(dbus_message_get_member(message), "add") == 0) {
|
||||
DebugF("config: adding device\n");
|
||||
/* signature should be [ss][ss]... */
|
||||
while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
|
||||
option = (InputOption *)xcalloc(sizeof(InputOption), 1);
|
||||
if (!option) {
|
||||
while (ret) {
|
||||
option = ret;
|
||||
ret = ret->next;
|
||||
xfree(option);
|
||||
}
|
||||
dbus_error_free(&error);
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
|
||||
dbus_message_iter_recurse(&iter, &subiter);
|
||||
if (strcmp(dbus_message_get_member(message), "add") == 0)
|
||||
ret = configAddDevice(message, &iter, &error);
|
||||
else if (strcmp(dbus_message_get_member(message), "remove") == 0)
|
||||
ret = configRemoveDevice(message, &iter, &error);
|
||||
}
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&subiter) !=
|
||||
DBUS_TYPE_STRING) {
|
||||
MALFORMED_MESSAGE();
|
||||
xfree(option);
|
||||
dbus_error_free(&error);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
dbus_message_iter_get_basic(&subiter, &tmp);
|
||||
if (!tmp) {
|
||||
MALFORMED_MESSAGE();
|
||||
xfree(option);
|
||||
break;
|
||||
}
|
||||
option->key = xstrdup(tmp);
|
||||
if (!option->key) {
|
||||
ErrorF("couldn't duplicate the key!\n");
|
||||
xfree(option);
|
||||
break;
|
||||
}
|
||||
if (ret != BadDrawable && ret != BadAlloc) {
|
||||
reply = dbus_message_new_method_return(message);
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
if (!dbus_message_iter_has_next(&subiter)) {
|
||||
MALFORMED_MESSAGE();
|
||||
xfree(option->key);
|
||||
xfree(option);
|
||||
dbus_error_free(&error);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
dbus_message_iter_next(&subiter);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&subiter) !=
|
||||
DBUS_TYPE_STRING) {
|
||||
MALFORMED_MESSAGE();
|
||||
xfree(option);
|
||||
dbus_error_free(&error);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
dbus_message_iter_get_basic(&subiter, &tmp);
|
||||
if (!tmp) {
|
||||
MALFORMED_MESSAGE();
|
||||
xfree(option->key);
|
||||
xfree(option);
|
||||
break;
|
||||
}
|
||||
option->value = xstrdup(tmp);
|
||||
if (!option->value) {
|
||||
ErrorF("couldn't duplicate the option!\n");
|
||||
xfree(option->value);
|
||||
xfree(option);
|
||||
break;
|
||||
}
|
||||
|
||||
option->next = ret;
|
||||
ret = option;
|
||||
dbus_message_iter_next(&iter);
|
||||
}
|
||||
|
||||
if (NewInputDeviceRequest(ret) != Success) {
|
||||
DebugF("config: NewInputDeviceRequest failed\n");
|
||||
}
|
||||
dbus_error_free(&error);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
else if (strcmp(dbus_message_get_member(message), "remove") == 0) {
|
||||
ErrorF("config: removing device\n");
|
||||
if (!dbus_message_get_args(message, &error, DBUS_TYPE_INT32,
|
||||
&deviceid, DBUS_TYPE_INVALID)) {
|
||||
MALFORMED_MESSAGE_ERROR();
|
||||
dbus_error_free(&error);
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
if (deviceid < 0 || !(pDev = LookupDeviceIntRec(deviceid))) {
|
||||
DebugF("config: bogus device id %d given\n", deviceid);
|
||||
dbus_error_free(&error);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
/* Call PIE here so we don't try to dereference a device that's
|
||||
* already been removed. Technically there's still a small race
|
||||
* here, so we should ensure that SIGIO is blocked. */
|
||||
ProcessInputEvents();
|
||||
RemoveDevice(pDev);
|
||||
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret)) {
|
||||
ErrorF("[config] couldn't append to iterator\n");
|
||||
dbus_error_free(&error);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
if (!dbus_connection_send(bus, reply, NULL))
|
||||
ErrorF("[config] failed to send reply\n");
|
||||
dbus_connection_flush(bus);
|
||||
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
dbus_error_free(&error);
|
||||
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
if (ret == BadAlloc)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
else if (ret == BadDrawable)
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
else
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -231,7 +296,7 @@ configInitialise()
|
|||
|
||||
vtable.message_function = configMessage;
|
||||
snprintf(busobject, sizeof(busobject), "/org/x/config/%d", atoi(display));
|
||||
if (!dbus_connection_register_object_path(bus, busobject, &vtable, NULL)) {
|
||||
if (!dbus_connection_register_object_path(bus, busobject, &vtable, bus)) {
|
||||
configfd = -1;
|
||||
dbus_bus_release_name(bus, busname, &error);
|
||||
dbus_bus_remove_match(bus, MATCH_RULE, &error);
|
||||
|
@ -243,9 +308,7 @@ configInitialise()
|
|||
DebugF("[dbus] registered object path %s\n", busobject);
|
||||
|
||||
dbus_error_free(&error);
|
||||
|
||||
configConnection = bus;
|
||||
|
||||
AddGeneralSocket(configfd);
|
||||
}
|
||||
|
||||
|
@ -265,7 +328,9 @@ configFini()
|
|||
dbus_error_free(&error);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
#else /* !HAVE_DBUS */
|
||||
|
||||
void
|
||||
configDispatch()
|
||||
{
|
||||
|
@ -280,4 +345,5 @@ void
|
|||
configFini()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_DBUS */
|
||||
|
|
35
config/dbus-api
Normal file
35
config/dbus-api
Normal file
|
@ -0,0 +1,35 @@
|
|||
D-BUS Configuration API v0.1
|
||||
----------------------------
|
||||
|
||||
The X server will register the bus name org.x.config.displayN, and the
|
||||
object /org/x/config/N, where N is the display number.
|
||||
|
||||
Currently only hotplugging of input devices is supported.
|
||||
|
||||
org.x.config.input:
|
||||
org.x.config.input.add:
|
||||
Takes an argument of key/value option pairs in arrays, e.g.:
|
||||
[ss][ss][ss][ss]
|
||||
is the signature for four options. These options will be passed
|
||||
to the input driver as with any others.
|
||||
Option names beginning with _ are not allowed; they are reserved
|
||||
for internal use.
|
||||
|
||||
Returns one int32, which is an X Status, as defined in X.h. If
|
||||
everything is successful, Success will be returned. BadMatch will
|
||||
be returned if the options given do not match any device. BadValue
|
||||
is returned for a malformed message.
|
||||
|
||||
Notably, BadAlloc is never returned: the server internally signals
|
||||
to D-BUS that the attempt failed for lack of memory.
|
||||
|
||||
The return does not notify the client of which devices were created
|
||||
or modified as a result of this request: clients are encouraged to
|
||||
listen for the XInput DevicePresenceNotify event to monitor changes
|
||||
in the device list.
|
||||
|
||||
org.x.config.input.remove:
|
||||
Takes one int32 argument, which is the device ID to remove, i.e.:
|
||||
i
|
||||
is the signature.
|
||||
Same return values as org.x.config.input.add.
|
Loading…
Reference in New Issue
Block a user