| /* |
| * vim:noexpandtab:shiftwidth=8:tabstop=8: |
| * |
| * Copyright (C) 2013, Panasas Inc. |
| * Contributor : Jim Lieb <jlieb@panasas.com> |
| * |
| * Some portions Copyright CEA/DAM/DIF (2008) |
| * contributeur : Philippe DENIEL philippe.deniel@cea.fr |
| * Thomas LEIBOVICI thomas.leibovici@cea.fr |
| * |
| * |
| * This program 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 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| * ------------- |
| */ |
| |
| #include "config.h" |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <time.h> |
| #include <pthread.h> |
| #include <assert.h> |
| #include "gsh_list.h" |
| #include "fsal.h" |
| #include "nfs_core.h" |
| #include "log.h" |
| #include "nfs_rpc_callback.h" |
| #include "gsh_dbus.h" |
| #include <os/memstream.h> |
| #include "dbus_priv.h" |
| |
| #ifndef DBUS_ERROR_UNKNOWN_INTERFACE |
| #define DBUS_ERROR_UNKNOWN_INTERFACE \ |
| "org.freedesktop.DBus.Error.UnknownInterface" |
| #endif |
| |
| #ifndef DBUS_ERROR_UNKNOWN_PROPERTY |
| #define DBUS_ERROR_UNKNOWN_PROPERTY \ |
| "org.freedesktop.DBus.Error.UnknownProperty" |
| #endif |
| |
| #ifndef DBUS_ERROR_PROPERTY_READ_ONLY |
| #define DBUS_ERROR_PROPERTY_READ_ONLY \ |
| "org.freedesktop.DBus.Error.PropertyReadOnly" |
| #endif |
| |
| /** |
| * @brief The properties interface for properties interfaces |
| * |
| * Seems if introspect says we have a properties interface for an |
| * object, scanners want to wall it to see if it has its own props. |
| * Fake one here because properties doesn't have properties; signals |
| * maybe but no props. |
| */ |
| |
| static struct gsh_dbus_interface props_interface = { |
| .name = DBUS_INTERFACE_PROPERTIES, |
| .props = NULL, |
| .methods = NULL, |
| .signals = NULL |
| }; |
| |
| static struct gsh_dbus_interface *props_ptr = &props_interface; |
| |
| static inline struct gsh_dbus_interface **lookup_interface(const char |
| *interface, |
| struct |
| gsh_dbus_interface |
| **interfaces, |
| DBusError *error) |
| { |
| struct gsh_dbus_interface **iface; |
| |
| if (strcmp(interface, DBUS_INTERFACE_PROPERTIES) == 0) |
| return &props_ptr; |
| for (iface = interfaces; *iface; iface++) { |
| if (strcmp(interface, (*iface)->name) == 0) |
| break; |
| } |
| if (*iface == NULL) { |
| dbus_set_error(error, DBUS_ERROR_UNKNOWN_INTERFACE, |
| "Requested interface: %s", interface); |
| } |
| return iface; |
| } |
| |
| static inline struct gsh_dbus_prop **lookup_property(const char *prop_name, |
| struct gsh_dbus_interface |
| **iface, |
| DBusError *error) |
| { |
| struct gsh_dbus_prop **prop; |
| |
| for (prop = (*iface)->props; prop && *prop; prop++) { |
| if (strcmp(prop_name, (*prop)->name) == 0) |
| break; |
| } |
| if (prop == NULL) { |
| dbus_set_error(error, DBUS_ERROR_UNKNOWN_PROPERTY, |
| "Requested property: %s from %s", prop_name, |
| (*iface)->name); |
| } |
| return prop; |
| } |
| |
| /** |
| * |
| * @brief Handle object properties |
| * |
| * Handle the three methods of the properties interface. |
| * |
| * @param argsp [IN] The message |
| * @param reply [IN/OUT] Our reply |
| * |
| * @return bool success or failure if bad request |
| */ |
| |
| bool dbus_proc_property(const char *method, DBusMessage *msg, |
| DBusMessage *reply, DBusError *error, |
| struct gsh_dbus_interface **interfaces) |
| { |
| const char *interface; |
| const char *prop_name; |
| bool retval = false; |
| struct gsh_dbus_interface **iface; |
| struct gsh_dbus_prop **prop; |
| DBusMessageIter reply_iter; |
| |
| dbus_message_iter_init_append(reply, &reply_iter); |
| |
| if (strcmp(method, "GetAll") == 0) { |
| DBusMessageIter getall_dict, dict_entry, val_iter; |
| |
| if (!dbus_message_get_args |
| (msg, error, DBUS_TYPE_STRING, &interface, |
| DBUS_TYPE_INVALID)) |
| goto err_out; |
| iface = lookup_interface(interface, interfaces, error); |
| if (*iface == NULL) |
| goto err_out; |
| if (!dbus_message_iter_open_container |
| (&reply_iter, DBUS_TYPE_ARRAY, |
| DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING |
| DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING |
| DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &getall_dict)) |
| goto getall_err; |
| for (prop = (*iface)->props; prop && *prop; prop++) { |
| prop_name = (*prop)->name; |
| if ((*prop)->access == DBUS_PROP_READ |
| || (*prop)->access == DBUS_PROP_READWRITE) { |
| if (!dbus_message_iter_open_container |
| (&getall_dict, DBUS_TYPE_DICT_ENTRY, NULL, |
| &dict_entry)) |
| goto getall_err; |
| if (!dbus_message_iter_append_basic |
| (&dict_entry, DBUS_TYPE_STRING, &prop_name)) |
| goto getall_err; |
| if (!dbus_message_iter_open_container |
| (&dict_entry, DBUS_TYPE_VARIANT, |
| (*prop)->type, &val_iter)) |
| goto getall_err; |
| if (!(*prop)->get(&val_iter)) |
| goto getall_err; |
| if (!dbus_message_iter_close_container |
| (&dict_entry, &val_iter)) |
| goto getall_err; |
| if (!dbus_message_iter_close_container |
| (&getall_dict, &dict_entry)) |
| goto getall_err; |
| } else { |
| dbus_set_error(error, |
| DBUS_ERROR_PROPERTY_READ_ONLY, |
| "%s of %s from %s (write only?)", |
| method, prop_name, interface); |
| /** @todo@ check does write only make sense?? */ |
| goto err_out; |
| } |
| } |
| if (!dbus_message_iter_close_container |
| (&reply_iter, &getall_dict)) |
| goto getall_err; |
| |
| return true; /* DONE! */ |
| |
| } else if (strcmp(method, "Get") == 0) { |
| if (!dbus_message_get_args |
| (msg, error, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, |
| &prop_name, DBUS_TYPE_INVALID)) |
| goto err_out; |
| iface = lookup_interface(interface, interfaces, error); |
| if (*iface == NULL) |
| goto err_out; |
| prop = lookup_property(prop_name, iface, error); |
| if (*prop == NULL) |
| goto err_out; |
| if ((*prop)->access == DBUS_PROP_READ |
| || (*prop)->access == DBUS_PROP_READWRITE) { |
| DBusMessageIter variant_iter; |
| |
| if (!dbus_message_iter_open_container |
| (&reply_iter, DBUS_TYPE_VARIANT, (*prop)->type, |
| &variant_iter)) { |
| dbus_set_error_const(error, DBUS_ERROR_FAILED, |
| "Couldn't open Get container"); |
| goto err_out; |
| } |
| retval = (*prop)->get(&variant_iter); |
| if (retval == false || |
| !dbus_message_iter_close_container(&reply_iter, |
| &variant_iter)) { |
| dbus_set_error_const(error, DBUS_ERROR_FAILED, |
| "Couldn't close Get container"); |
| goto err_out; |
| } |
| } else { |
| dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, |
| "%s of %s from %s (write only?)", method, |
| prop_name, interface); |
| /** @todo@ check does write only make sense?? */ |
| goto err_out; |
| } |
| |
| return true; /* DONE! */ |
| |
| } else if (strcmp(method, "Set") == 0) { |
| DBusMessageIter iter_args; |
| |
| if (!dbus_message_iter_init(msg, &iter_args) |
| || dbus_message_iter_get_arg_type(&iter_args) != |
| DBUS_TYPE_STRING) { |
| goto invalid_args; |
| } |
| dbus_message_iter_get_basic(&iter_args, &interface); |
| if (!dbus_message_iter_next(&iter_args) |
| || dbus_message_iter_get_arg_type(&iter_args) != |
| DBUS_TYPE_STRING) { |
| goto invalid_args; |
| } |
| dbus_message_iter_get_basic(&iter_args, &prop_name); |
| if (!dbus_message_iter_next(&iter_args) |
| || dbus_message_iter_get_arg_type(&iter_args) != |
| DBUS_TYPE_VARIANT |
| || dbus_message_iter_has_next(&iter_args)) { |
| goto invalid_args; |
| } |
| iface = lookup_interface(interface, interfaces, error); |
| if (*iface == NULL) |
| goto err_out; |
| prop = lookup_property(prop_name, iface, error); |
| if (*prop == NULL) |
| goto err_out; |
| if ((*prop)->access == DBUS_PROP_WRITE |
| || (*prop)->access == DBUS_PROP_READWRITE) { |
| DBusMessageIter arg; |
| |
| dbus_message_iter_recurse(&iter_args, &arg); |
| |
| return (*prop)->set(&arg); /* DONE! */ |
| |
| } else { |
| dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, |
| "%s of %s from %s", method, prop_name, |
| interface); |
| goto err_out; |
| } |
| } else { |
| dbus_set_error(error, DBUS_ERROR_UNKNOWN_METHOD, |
| "Requested method: %s", method); |
| } |
| return retval; |
| |
| getall_err: |
| dbus_set_error(error, DBUS_ERROR_FAILED, "GetAll container failure"); |
| goto err_out; |
| |
| invalid_args: |
| dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "Method %s", method); |
| |
| err_out: |
| return retval; |
| } |