diff --git a/Makefile.am b/Makefile.am index bf8b1b53..52c0acf6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -80,7 +80,8 @@ bin_PROGRAMS += \ ratbagd_SOURCES = \ src/ratbagd.h \ src/ratbagd.c \ - src/ratbagd-device.c + src/ratbagd-device.c \ + src/ratbagd-profile.c ratbagd_CFLAGS = \ $(AM_CFLAGS) \ diff --git a/src/ratbagd-device.c b/src/ratbagd-device.c index cae64b62..f82dcf33 100644 --- a/src/ratbagd-device.c +++ b/src/ratbagd-device.c @@ -40,11 +40,83 @@ struct ratbagd_device { RBNode node; char *name; struct ratbag_device *lib_device; + + sd_bus_slot *profile_vtable_slot; + sd_bus_slot *profile_enum_slot; + unsigned int n_profiles; + struct ratbagd_profile **profiles; }; #define ratbagd_device_from_node(_ptr) \ rbnode_of((_ptr), struct ratbagd_device, node) +static int ratbagd_device_find_profile(sd_bus *bus, + const char *path, + const char *interface, + void *userdata, + void **found, + sd_bus_error *error) +{ + _cleanup_(freep) char *name = NULL; + struct ratbagd_device *device = userdata; + unsigned int index; + int r; + + r = sd_bus_path_decode_many(path, + "/org/freedesktop/ratbag1/device/%/profile/%", + NULL, + &name); + if (r <= 0) + return r; + + r = safe_atou(name, &index); + if (r < 0) + return 0; + + if (index >= device->n_profiles) + return 0; + + *found = device->profiles[index]; + return 1; +} + +static int ratbagd_device_list_profiles(sd_bus *bus, + const char *path, + void *userdata, + char ***paths, + sd_bus_error *error) +{ + struct ratbagd_device *device = userdata; + char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1]; + char **profiles; + unsigned int i; + int r; + + profiles = calloc(device->n_profiles + 1, sizeof(char *)); + if (!profiles) + return -ENOMEM; + + for (i = 0; i < device->n_profiles; ++i) { + sprintf(index_buffer, "%u", i); + r = sd_bus_path_encode_many(&profiles[i], + "/org/freedesktop/ratbag1/device/%/profile/%", + device->name, + index_buffer); + if (r < 0) + goto error; + } + + profiles[i] = NULL; + *paths = profiles; + return 1; + +error: + for (i = 0; profiles[i]; ++i) + free(profiles[i]); + free(profiles); + return r; +} + static int ratbagd_device_get_description(sd_bus *bus, const char *path, const char *interface, @@ -76,6 +148,9 @@ int ratbagd_device_new(struct ratbagd_device **out, struct ratbag_device *lib_device) { _cleanup_(ratbagd_device_freep) struct ratbagd_device *device = NULL; + struct ratbag_profile *profile; + unsigned int i; + int r; assert(out); assert(ctx); @@ -93,6 +168,25 @@ int ratbagd_device_new(struct ratbagd_device **out, if (!device->name) return -ENOMEM; + device->n_profiles = ratbag_device_get_num_profiles(device->lib_device); + device->profiles = calloc(device->n_profiles, sizeof(*device->profiles)); + if (!device->profiles) + return -ENOMEM; + + for (i = 0; i < device->n_profiles; ++i) { + profile = ratbag_device_get_profile(device->lib_device, i); + if (!profile) + continue; + + r = ratbagd_profile_new(&device->profiles[i], profile, i); + if (r < 0) { + errno = -r; + fprintf(stderr, + "Cannot allocate profile for '%s': %m\n", + device->name); + } + } + *out = device; device = NULL; return 0; @@ -100,11 +194,17 @@ int ratbagd_device_new(struct ratbagd_device **out, struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device) { + unsigned int i; + if (!device) return NULL; assert(!ratbagd_device_linked(device)); + for (i = 0; i < device->n_profiles; ++i) + device->profiles[i] = ratbagd_profile_free(device->profiles[i]); + + device->profiles = mfree(device->profiles); device->lib_device = ratbag_device_unref(device->lib_device); device->name = mfree(device->name); @@ -120,7 +220,7 @@ bool ratbagd_device_linked(struct ratbagd_device *device) void ratbagd_device_link(struct ratbagd_device *device) { - _cleanup_(freep) char *path = NULL; + _cleanup_(freep) char *path = NULL, *prefix = NULL; struct ratbagd_device *iter; RBNode **node, *parent; int r, v; @@ -161,6 +261,32 @@ void ratbagd_device_link(struct ratbagd_device *device) "Cannot send device notification for '%s': %m\n", device->name); } + + /* register profile interfaces */ + r = sd_bus_path_encode_many(&prefix, + "/org/freedesktop/ratbag1/device/%/profile", + device->name); + if (r >= 0) { + r = sd_bus_add_fallback_vtable(device->ctx->bus, + &device->profile_vtable_slot, + prefix, + "org.freedesktop.ratbag1.Profile", + ratbagd_profile_vtable, + ratbagd_device_find_profile, + device); + if (r >= 0) + r = sd_bus_add_node_enumerator(device->ctx->bus, + &device->profile_enum_slot, + prefix, + ratbagd_device_list_profiles, + device); + } + if (r < 0) { + errno = -r; + fprintf(stderr, + "Cannot register profile interfaces for '%s': %m\n", + device->name); + } } void ratbagd_device_unlink(struct ratbagd_device *device) @@ -171,6 +297,9 @@ void ratbagd_device_unlink(struct ratbagd_device *device) if (!ratbagd_device_linked(device)) return; + device->profile_enum_slot = sd_bus_slot_unref(device->profile_enum_slot); + device->profile_vtable_slot = sd_bus_slot_unref(device->profile_vtable_slot); + /* send out object-manager notification */ r = sd_bus_path_encode("/org/freedesktop/ratbag1/device", device->name, diff --git a/src/ratbagd-profile.c b/src/ratbagd-profile.c new file mode 100644 index 00000000..ad7a9d24 --- /dev/null +++ b/src/ratbagd-profile.c @@ -0,0 +1,79 @@ +/*** + This file is part of ratbagd. + + Copyright 2015 David Herrmann + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ratbagd.h" +#include "shared-macro.h" + +struct ratbagd_profile { + struct ratbag_profile *lib_profile; + unsigned int index; +}; + +const sd_bus_vtable ratbagd_profile_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_profile, index), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_VTABLE_END, +}; + +int ratbagd_profile_new(struct ratbagd_profile **out, + struct ratbag_profile *lib_profile, + unsigned int index) +{ + _cleanup_(ratbagd_profile_freep) struct ratbagd_profile *profile = NULL; + + assert(out); + assert(lib_profile); + + profile = calloc(1, sizeof(*profile)); + if (!profile) + return -ENOMEM; + + profile->lib_profile = lib_profile; + profile->index = index; + + *out = profile; + profile = NULL; + return 0; +} + +struct ratbagd_profile *ratbagd_profile_free(struct ratbagd_profile *profile) +{ + if (!profile) + return NULL; + + /* we cannot assume the profile is gone, so set NULL explicitly */ + ratbag_profile_unref(profile->lib_profile); + profile->lib_profile = NULL; + + return mfree(profile); +} diff --git a/src/ratbagd.h b/src/ratbagd.h index d86c1fe0..8a58898c 100644 --- a/src/ratbagd.h +++ b/src/ratbagd.h @@ -38,6 +38,20 @@ struct ratbagd; struct ratbagd_device; +struct ratbagd_profile; + +/* + * Profiles + */ + +extern const sd_bus_vtable ratbagd_profile_vtable[]; + +int ratbagd_profile_new(struct ratbagd_profile **out, + struct ratbag_profile *lib_profile, + unsigned int index); +struct ratbagd_profile *ratbagd_profile_free(struct ratbagd_profile *profile); + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_profile *, ratbagd_profile_free); /* * Devices diff --git a/src/shared-macro.h b/src/shared-macro.h index 05ec2f3b..748d4292 100644 --- a/src/shared-macro.h +++ b/src/shared-macro.h @@ -74,6 +74,18 @@ #define _weak_ __attribute__((weak)) #define _weakref_(_x) __attribute__((weakref(#_x))) +/* + * DECIMAL_TOKEN_MAX() - calculate maximum length of the decimal representation + * of an integer + * @_type: type of integer + */ +#define DECIMAL_TOKEN_MAX(_type) \ + (1 + (sizeof(_type) <= 1 ? 3 : \ + sizeof(_type) <= 2 ? 5 : \ + sizeof(_type) <= 4 ? 10 : \ + sizeof(_type) <= 8 ? 20 : \ + sizeof(int[-2 * (sizeof(_type) > 8)]))) + /* * PROTECT_ERRNO: make sure a function protects errno */ @@ -204,6 +216,36 @@ static inline char *startswith(const char *s, const char *prefix) return NULL; } +/* + * safe_atou() - safe variant of strtoul() + * @s: string to parse + * @ret_u: output storage for parsed integer + * + * This is a sane and safe variant of strtoul(), which rejects any invalid + * input, or trailing garbage. + * + * Return: 0 on success, negative error code on failure. + */ +static inline int safe_atou(const char *s, unsigned int *ret_u) +{ + char *x = NULL; + unsigned long l; + + assert(s); + assert(ret_u); + + errno = 0; + l = strtoul(s, &x, 0); + if (!x || x == s || *x || errno) + return errno > 0 ? -errno : -EINVAL; + + if ((unsigned long)(unsigned int)l != l) + return -ERANGE; + + *ret_u = (unsigned int)l; + return 0; +} + /* * now() - returns current time in nano-seconds * @clock: clock to use