/* Authors: Stef Walter Pavel Březina Copyright (C) 2014 Red Hat This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #include "util/util.h" #include "sbus/sssd_dbus.h" #include "sbus/sssd_dbus_meta.h" #include "sbus/sssd_dbus_private.h" #include "sbus/sssd_dbus_invokers.h" #define CHECK_SIGNATURE_OR_FAIL(req, error, label, exp) do { \ const char *__sig; \ __sig = dbus_message_get_signature(req->message); \ if (strcmp(__sig, exp) != 0) { \ error = sbus_error_new(req, DBUS_ERROR_INVALID_ARGS, \ "Invalid arguments: expected \"%s\", got \"%s\"", exp, __sig); \ goto label; \ } \ } while (0) struct iface_properties { struct sbus_vtable vtable; /* derive from sbus_vtable */ sbus_msg_handler_fn Get; sbus_msg_handler_fn Set; sbus_msg_handler_fn GetAll; }; static int sbus_properties_get(struct sbus_request *sbus_req, void *pvt); static int sbus_properties_set(struct sbus_request *sbus_req, void *pvt); static int sbus_properties_get_all(struct sbus_request *sbus_req, void *pvt); struct sbus_vtable * sbus_properties_vtable(void) { /* Properties.Get */ static const struct sbus_arg_meta get_args_in[] = { { "interface_name", "s" }, { "property_name", "s" }, { NULL, } }; static const struct sbus_arg_meta get_args_out[] = { { "value", "v" }, { NULL, } }; /* Properties.Set */ static const struct sbus_arg_meta set_args_in[] = { { "interface_name", "s" }, { "property_name", "s" }, { "value", "v" }, { NULL, } }; /* Properties.GetAll */ static const struct sbus_arg_meta getall_args_in[] = { { "interface_name", "s" }, { NULL, } }; static const struct sbus_arg_meta getall_args_out[] = { { "props", "a{sv}" }, { NULL, } }; static const struct sbus_method_meta iface_methods[] = { { "Get", /* name */ get_args_in, get_args_out, offsetof(struct iface_properties, Get), NULL, /* no invoker */ }, { "Set", /* name */ set_args_in, NULL, /* no out_args */ offsetof(struct iface_properties, Set), NULL, /* no invoker */ }, { "GetAll", /* name */ getall_args_in, getall_args_out, offsetof(struct iface_properties, GetAll), NULL, /* no invoker */ }, { NULL, } }; static const struct sbus_interface_meta iface_meta = { "org.freedesktop.DBus.Properties", /* name */ iface_methods, NULL, /* no signals */ NULL, /* no properties */ NULL, /* no GetAll invoker */ }; static struct iface_properties iface = { { &iface_meta, 0 }, .Get = sbus_properties_get, .Set = sbus_properties_set, .GetAll = sbus_properties_get_all, }; return &iface.vtable; } static struct sbus_request * sbus_properties_subreq(struct sbus_request *sbus_req, struct sbus_interface *iface) { struct sbus_request *sbus_subreq; /* Create new sbus_request to so it contain given interface. The * old sbus_request talloc context will be attached to this new one * so it is freed together. */ sbus_subreq = sbus_new_request(sbus_req->conn, iface, sbus_req->message); if (sbus_subreq == NULL) { return NULL; } talloc_steal(sbus_subreq, sbus_req); return sbus_subreq; } static int sbus_properties_get(struct sbus_request *sbus_req, void *pvt) { DBusError *error; struct sbus_request *sbus_subreq; struct sbus_connection *conn; struct sbus_interface *iface; const struct sbus_property_meta *prop; sbus_msg_handler_fn handler_fn; const char *interface_name; const char *property_name; bool bret; conn = talloc_get_type(pvt, struct sbus_connection); CHECK_SIGNATURE_OR_FAIL(sbus_req, error, fail, "ss"); bret = sbus_request_parse_or_finish(sbus_req, DBUS_TYPE_STRING, &interface_name, DBUS_TYPE_STRING, &property_name, DBUS_TYPE_INVALID); if (!bret) { /* request was handled */ return EOK; } /* find interface */ iface = sbus_opath_hash_lookup_iface(conn->managed_paths, sbus_req->path, interface_name); if (iface == NULL) { error = sbus_error_new(sbus_req, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); goto fail; } /* find property handler */ prop = sbus_meta_find_property(iface->vtable->meta, property_name); if (prop == NULL) { error = sbus_error_new(sbus_req, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); goto fail; } if (!(prop->flags & SBUS_PROPERTY_READABLE)) { error = sbus_error_new(sbus_req, DBUS_ERROR_ACCESS_DENIED, "Property is not readable"); goto fail; } handler_fn = VTABLE_FUNC(iface->vtable, prop->vtable_offset_get); if (handler_fn == NULL) { error = sbus_error_new(sbus_req, DBUS_ERROR_NOT_SUPPORTED, "Getter is not implemented"); goto fail; } sbus_subreq = sbus_properties_subreq(sbus_req, iface); if (sbus_subreq == NULL) { error = NULL; goto fail; } sbus_invoke_get(sbus_subreq, prop->type, prop->invoker_get, handler_fn); return EOK; fail: return sbus_request_fail_and_finish(sbus_req, error); } /* * We don't implement any handlers for setters yet. This code is for future * use and it is likely it will need some changes. */ static int sbus_properties_set(struct sbus_request *sbus_req, void *pvt) { DBusError *error; DBusMessageIter iter; DBusMessageIter iter_variant; struct sbus_request *sbus_subreq; struct sbus_connection *conn; struct sbus_interface *iface; const struct sbus_property_meta *prop; const char *interface_name; const char *property_name; const char *variant_sig; sbus_msg_handler_fn handler_fn; conn = talloc_get_type(pvt, struct sbus_connection); CHECK_SIGNATURE_OR_FAIL(sbus_req, error, fail, "ssv"); /* get interface and property */ dbus_message_iter_init(sbus_req->message, &iter); dbus_message_iter_get_basic(&iter, &interface_name); dbus_message_iter_next(&iter); dbus_message_iter_get_basic(&iter, &property_name); dbus_message_iter_next(&iter); /* find interface */ iface = sbus_opath_hash_lookup_iface(conn->managed_paths, sbus_req->path, interface_name); if (iface == NULL) { error = sbus_error_new(sbus_req, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); goto fail; } /* find property handler */ prop = sbus_meta_find_property(iface->vtable->meta, property_name); if (prop == NULL) { error = sbus_error_new(sbus_req, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); goto fail; } if (!(prop->flags & SBUS_PROPERTY_WRITABLE)) { error = sbus_error_new(sbus_req, DBUS_ERROR_ACCESS_DENIED, "Property is not writable"); goto fail; } handler_fn = VTABLE_FUNC(iface->vtable, prop->vtable_offset_set); if (handler_fn == NULL) { error = sbus_error_new(sbus_req, DBUS_ERROR_NOT_SUPPORTED, "Setter is not implemented"); goto fail; } /* check variant type */ dbus_message_iter_recurse(&iter, &iter_variant); variant_sig = dbus_message_iter_get_signature(&iter_variant); if (strcmp(prop->type, variant_sig) != 0) { error = sbus_error_new(sbus_req, DBUS_ERROR_INVALID_ARGS, "Invalid data type for property"); goto fail; } sbus_subreq = sbus_properties_subreq(sbus_req, iface); if (sbus_subreq == NULL) { error = NULL; goto fail; } sbus_request_invoke_or_finish(sbus_subreq, handler_fn, iface->handler_data, prop->invoker_set); return EOK; fail: return sbus_request_fail_and_finish(sbus_req, error); } static int sbus_properties_get_all(struct sbus_request *sbus_req, void *pvt) { DBusError *error; struct sbus_request *sbus_subreq; struct sbus_connection *conn; struct sbus_interface *iface; const char *interface_name; bool bret; conn = talloc_get_type(pvt, struct sbus_connection); CHECK_SIGNATURE_OR_FAIL(sbus_req, error, fail, "s"); bret = sbus_request_parse_or_finish(sbus_req, DBUS_TYPE_STRING, &interface_name, DBUS_TYPE_INVALID); if (!bret) { /* request was handled */ return EOK; } /* find interface */ iface = sbus_opath_hash_lookup_iface(conn->managed_paths, sbus_req->path, interface_name); if (iface == NULL) { error = sbus_error_new(sbus_req, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); goto fail; } sbus_subreq = sbus_properties_subreq(sbus_req, iface); if (sbus_subreq == NULL) { error = NULL; goto fail; } if (iface->vtable->meta->invoker_get_all == NULL) { DEBUG(SSSDBG_TRACE_FUNC, "No get all invoker set," "using the default one\n"); sbus_invoke_get_all(sbus_req); } else { iface->vtable->meta->invoker_get_all(sbus_subreq); } return EOK; fail: return sbus_request_fail_and_finish(sbus_req, error); }