From 4670ca0016debf993f7469b457ef8b3007d058b6 Mon Sep 17 00:00:00 2001 From: Ceara Chewning Date: Fri, 5 Sep 2014 11:07:17 -0700 Subject: [PATCH 1/2] Implemented get_key_type feature in libbuxton, daemon, and buxtonctl Get_key_type returns the type of a key's value as a uint32_t, which can be cast to a BuxtonDataType. This commit adds buxton_get_key_type to libbuxton, adds support functions for it in protocol, daemon, direct, and gdbm, adds this feature to buxtonctl, and adds a new BuxtonDataType UNKNOWN. A demo program, bxt_hello_get_key_type, a man page, buxton_get_key_type, and changes to other affected man pages are included. Passing unit tests are in check_daemon and check_buxton. Calls to buxton_get_key_type must have their key type set to UNKNOWN. --- Makefile.am | 9 ++ demo/hellogetkeytype.c | 171 ++++++++++++++++++++ docs/buxton-api.7 | 3 + docs/buxton_get_key_type.3 | 218 ++++++++++++++++++++++++++ docs/buxtonctl.1 | 5 + src/cli/client.c | 131 ++++++++++++++++ src/cli/client.h | 15 ++ src/cli/main.c | 6 + src/core/daemon.c | 88 +++++++++++ src/core/daemon.h | 12 ++ src/db/gdbm.c | 76 +++++++++ src/db/memory.c | 62 ++++++++ src/include/buxton.h | 18 +++ src/libbuxton/lbuxton.c | 48 +++++- src/libbuxton/lbuxton.sym | 1 + src/shared/backend.c | 1 + src/shared/backend.h | 1 + src/shared/direct.c | 7 +- src/shared/direct.h | 32 ++++ src/shared/protocol.c | 58 +++++++ src/shared/protocol.h | 12 ++ test/check_buxton.c | 147 ++++++++++++++++++ test/check_daemon.c | 309 +++++++++++++++++++++++++++++++++++++ 23 files changed, 1423 insertions(+), 7 deletions(-) create mode 100644 demo/hellogetkeytype.c create mode 100644 docs/buxton_get_key_type.3 diff --git a/Makefile.am b/Makefile.am index b3d8baa..28a6fcf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -78,6 +78,7 @@ dist_man_MANS = \ docs/buxton_client_handle_response.3 \ docs/buxton_close.3 \ docs/buxton_create_group.3 \ + docs/buxton_get_key_type.3 \ docs/buxton_get_value.3 \ docs/buxton_key_create.3 \ docs/buxton_key_free.3 \ @@ -448,6 +449,7 @@ check_DATA = \ if BUILD_DEMOS bin_PROGRAMS += \ bxt_timing \ + bxt_hello_get_key_type \ bxt_hello_get \ bxt_hello_set \ bxt_hello_set_label \ @@ -466,6 +468,13 @@ bxt_timing_LDADD = \ libbuxton-shared.la \ -lrt -lm +bxt_hello_get_key_type_SOURCES = \ + demo/hellogetkeytype.c +bxt_hello_get_key_type_CFLAGS = \ + $(AM_CFLAGS) +bxt_hello_get_key_type_LDADD =\ + libbuxton.la + bxt_hello_get_SOURCES = \ demo/helloget.c bxt_hello_get_CFLAGS = \ diff --git a/demo/hellogetkeytype.c b/demo/hellogetkeytype.c new file mode 100644 index 0000000..09c8bf6 --- /dev/null +++ b/demo/hellogetkeytype.c @@ -0,0 +1,171 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2013 Intel Corporation + * + * 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 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. + * + */ + +/* + * Run this demo after creating the group with bxt_hello_create_group + * and after setting the key with bxt_hello_set + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "buxton.h" + +void get_cb(BuxtonResponse response, void *data) +{ + BuxtonDataType *ret = (BuxtonDataType*) data; + + if (buxton_response_status(response) != 0) { + + printf("Failed to get value\n"); + return; + } else { + printf("Get successful, got type\n"); + void *p = buxton_response_value(response); + *ret = *(BuxtonDataType*)p; + return; + } +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + BuxtonDataType d_type = BUXTON_TYPE_MIN; + int fd; + char *type; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\n"); + return -1; + } + +/* + * A fully qualified key-name is being created since both group and key-name are not null. + * Group: "hello", Key-name: "test", Layer: "user", DataType: UNKNOWN + */ + key = buxton_key_create("hello", "test", "user", UNKNOWN); + if (!key) { + return -1; + } + + if (buxton_get_key_type(client, key, get_cb, + &d_type, false)) { + printf("get call failed to run\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\n"); + return -1; + } + + switch (d_type) { + case BUXTON_TYPE_MIN: + { + type = "invalid- still min"; + } + case STRING: + { + type = "string"; + break; + } + case INT32: + { + type = "int32_t"; + break; + } + case UINT32: + { + type = "uint32_t"; + break; + } + case INT64: + { + type = "int64_t"; + break; + } + case UINT64: + { + type = "uint64_t"; + break; + } + case FLOAT: + { + type = "float"; + break; + } + case DOUBLE: + { + type = "double"; + break; + } + case BOOLEAN: + { + type = "bool"; + break; + } + default: + { + type = "unknown"; + break; + } + } + + printf("type of key is: %d = %s\n", d_type, type); + + buxton_key_free(key); + buxton_close(client); + return 0; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/docs/buxton-api.7 b/docs/buxton-api.7 index 967c31b..9e875fb 100644 --- a/docs/buxton-api.7 +++ b/docs/buxton-api.7 @@ -82,6 +82,9 @@ use these API functions\&. \fBbuxton_get_value\fR(3) \(em Get the value of a key .br +\fBbuxton_get_key_type\fR(3) +\(em Get the type of a key +.br \fBbuxton_unset_value\fR(3) \(em Unset the value for a key .br diff --git a/docs/buxton_get_key_type.3 b/docs/buxton_get_key_type.3 new file mode 100644 index 0000000..e77f165 --- /dev/null +++ b/docs/buxton_get_key_type.3 @@ -0,0 +1,218 @@ +'\" t +.TH "BUXTON_GET_KEY_TYPE" "3" "buxton 1" "buxton_get_key_type" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +buxton_get_key_type \- Get the type of value for a key\-name + +.SH "SYNOPSIS" +.nf +\fB +#include +\fR +.sp +\fB +int buxton_get_key_type(BuxtonClient \fIclient\fB, +.br + BuxtonKey \fIkey\fB, +.br + BuxtonCallback \fIcallback\fB, +.br + void *\fIdata\fB, +.br + bool \fIsync\fB) +\fR +.fi + +.SH "DESCRIPTION" +.PP +This function is used to get the type of value for a key\-name for +\fIclient\fR. The key\-name is referenced by \fIkey\fR. The key\-type +must be set to UNKNOWN. If the layer +for \fIkey\fR is NULL, buxton will traverse layers in priority order +searching for the key-name value, selecting the value for the first +key\-name found\&. If the argument is non-NULL, the operation will +target only that layer\&. For more information on creating a +BuxtonKey to pass for \fIkey\fR, see \fBbuxton_key_create\fR(3)\&. + +To retrieve the result of the operation, clients should define a +callback function, referenced by the \fIcallback\fR argument; the +callback function is called upon completion of the operation\&. The +\fIdata\fR argument is a pointer to arbitrary userdata that is passed +along to the callback function\&. Additonally, the \fIsync\fR +argument controls whether the operation should be synchronous or not; +if \fIsync\fR is false, the operation is asynchronous\&. + +.SH "CODE EXAMPLE" +.nf +.sp +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "buxton.h" + +void get_cb(BuxtonResponse response, void *data) +{ + BuxtonDataType *ret = (BuxtonDataType*) data; + + if (buxton_response_status(response) != 0) { + + printf("Failed to get value\\n"); + return; + } else { + printf("Get successful, got type\\n"); + void *p = buxton_response_value(response); + *ret = *(BuxtonDataType*)p; + return; + } +} + +int main(void) +{ + BuxtonClient client; + BuxtonKey key; + struct pollfd pfd[1]; + int r; + BuxtonDataType d_type = BUXTON_TYPE_MIN; + int fd; + char *type; + + if ((fd = buxton_open(&client)) < 0) { + printf("couldn't connect\\n"); + return -1; + } + +/* + * A fully qualified key-name is being created since both group and key-name are not null. + * Group: "hello", Key-name: "test", Layer: "user", DataType: UNKNOWN + */ + key = buxton_key_create("hello", "test", "user", UNKNOWN); + if (!key) { + return -1; + } + + if (buxton_get_key_type(client, key, get_cb, + &d_type, false)) { + printf("get call failed to run\\n"); + return -1; + } + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r <= 0) { + printf("poll error\n"); + return -1; + } + + if (!buxton_client_handle_response(client)) { + printf("bad response from daemon\\n"); + return -1; + } + + switch (d_type) { + case BUXTON_TYPE_MIN: + { + type = "invalid- still min"; + } + case STRING: + { + type = "string"; + break; + } + case INT32: + { + type = "int32_t"; + break; + } + case UINT32: + { + type = "uint32_t"; + break; + } + case INT64: + { + type = "int64_t"; + break; + } + case UINT64: + { + type = "uint64_t"; + break; + } + case FLOAT: + { + type = "float"; + break; + } + case DOUBLE: + { + type = "double"; + break; + } + case BOOLEAN: + { + type = "bool"; + break; + } + default: + { + type = "unknown"; + break; + } + } + + printf("type of key is: %d = %s\\n", d_type, type); + + buxton_key_free(key); + buxton_close(client); + return 0; +} +.fi + +.SH "RETURN VALUE" +.PP +Returns 0 on success, and a non\-zero value on failure\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception +for code examples found in the \fBCODE EXAMPLE\fR section, which are +licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR +file from this buxton distribution\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/buxtonctl.1 b/docs/buxtonctl.1 index 3dd95a9..c82e8e3 100644 --- a/docs/buxtonctl.1 +++ b/docs/buxtonctl.1 @@ -162,6 +162,11 @@ Gets a key value with boolean type\&. Sets a key value with boolean type\&. .RE .PP +\fBget\-key\-type\fR [LAYER] GROUP KEY +.RS 4 +Gets the type of value for a key +.RE +.PP \fBset\-label\fR LAYER GROUP KEY LABEL .RS 4 Sets the Smack label on a key\&. Note that this is a privileged diff --git a/src/cli/client.c b/src/cli/client.c index decb4dd..864eb2b 100644 --- a/src/cli/client.c +++ b/src/cli/client.c @@ -335,6 +335,137 @@ bool cli_set_value(BuxtonControl *control, BuxtonDataType type, return ret; } +void get_key_type_callback(BuxtonResponse response, void *data) +{ + BuxtonData *r = (BuxtonData *)data; + void *p; + + if (buxton_response_status(response) != 0) { + return; + } + + p = buxton_response_value(response); + if (!p) { + return; + } + + r->type = UINT32; + r->store.d_uint32 = *(uint32_t *)p; +} + +bool cli_get_key_type(BuxtonControl *control, BuxtonDataType type, + char *one, char *two, char *three, __attribute__((unused)) char * four) +{ + BuxtonKey key; + BuxtonData get; + _cleanup_free_ char *prefix = NULL; + _cleanup_free_ char *group = NULL; + _cleanup_free_ char *name = NULL; + BuxtonString dlabel; + bool ret = false; + int32_t ret_val; + int r; + + memzero((void *)&get, sizeof(BuxtonData)); + if (three != NULL) { + key = buxton_key_create(two, three, one, type); + r = asprintf(&prefix, "[%s]", one); + if (!r) { + abort(); + } + } else { + key = buxton_key_create(one, two, NULL, type); + r = asprintf(&prefix, " "); + if (!r) { + abort(); + } + } + + if (!key) { + return false; + } + + if (three != NULL) { + if (control->client.direct) { + ret = buxton_direct_get_value_for_layer(control, key, + &get, &dlabel, + NULL); + } else { + ret = buxton_get_key_type(&control->client, key, + get_key_type_callback, + &get, true); + } + if (ret) { + group = get_group(key); + name = get_name(key); + printf("Requested key was not found in layer \'%s\': %s:%s\n", + one, nv(group), nv(name)); + return false; + } + } else { + if (control->client.direct) { + ret_val = buxton_direct_get_value(control, key, &get, + &dlabel, NULL); + if (ret_val == 0) { + ret = true; + } + } else { + ret = buxton_get_key_type(&control->client, key, + get_key_type_callback, + &get, true); + } + if (ret) { + group = get_group(key); + name = get_name(key); + printf("Requested key was not found: %s:%s\n", nv(group), + nv(name)); + return false; + } + } + + group = get_group(key); + name = get_name(key); + if (get.type != UINT32) { + printf("Get Key Type did not return a BuxtonDataType.\n"); + return false; + } + + switch ((BuxtonDataType)get.store.d_uint32) { + case STRING: + printf("%s%s:%s = STRING \n", prefix, nv(group), nv(name)); + break; + case INT32: + printf("%s%s:%s = INT32 \n", prefix, nv(group), nv(name)); + break; + case UINT32: + printf("%s%s:%s = UINT32 \n", prefix, nv(group), nv(name)); + break; + case INT64: + printf("%s%s:%s = INT64 \n", prefix, nv(group), nv(name)); + break; + case UINT64: + printf("%s%s:%s = UINT64 \n", prefix, nv(group), nv(name)); + break; + case FLOAT: + printf("%s%s:%s = FLOAT \n", prefix, nv(group), nv(name)); + break; + case DOUBLE: + printf("%s%s:%s = DOUBLE \n", prefix, nv(group), nv(name)); + break; + case BOOLEAN: + printf("%s%s:%s = BOOLEAN \n", prefix, nv(group), nv(name)); + break; + case BUXTON_TYPE_MIN: + printf("Requested key was not found: %s:%s\n", nv(group), nv(name)); + return false; + default: + printf("unknown type\n"); + return false; + } + + return true; +} + void get_value_callback(BuxtonResponse response, void *data) { BuxtonKey key; diff --git a/src/cli/client.h b/src/cli/client.h index 05adc36..8e03878 100644 --- a/src/cli/client.h +++ b/src/cli/client.h @@ -142,6 +142,21 @@ bool cli_set_value(BuxtonControl *control, BuxtonDataType type, char *one, char *two, char *three, char *four) __attribute__((warn_unused_result)); +/** + * Get a key type from Buxton + * @param control An initialized control structure + * @param type Type of key used to request type + * @param one Layer or Group of data being set + * @param two or key of data being set + * @param two Key if one is Layer + * @param three NULL (unused) + * @returns bool indicating success or failure + */ +bool cli_get_key_type(BuxtonControl *control, BuxtonDataType type, char *one, + char *two, char *three, + __attribute__((unused)) char *four) + __attribute__((warn_unused_result)); + /** * Get a value from Buxton * @param control An initialized control structure diff --git a/src/cli/main.c b/src/cli/main.c index 97788b8..ddce84d 100644 --- a/src/cli/main.c +++ b/src/cli/main.c @@ -90,6 +90,7 @@ int main(int argc, char **argv) Command c_get_float, c_set_float; Command c_get_double, c_set_double; Command c_get_bool, c_set_bool; + Command c_get_key_type; Command c_set_label; Command c_create_group, c_remove_group; Command c_unset_value; @@ -186,6 +187,11 @@ int main(int argc, char **argv) 4, 4, "layer group name value", &cli_set_value, BOOLEAN }; hashmap_put(commands, c_set_bool.name, &c_set_bool); + /* Get key type */ + c_get_key_type = (Command) { "get-key-type", "Get the data type of a key", + 2, 3, "[layer] group name", &cli_get_key_type, UNKNOWN }; + hashmap_put(commands, c_get_key_type.name, &c_get_key_type); + /* SMACK labels */ c_set_label = (Command) { "set-label", "Set a value's label", 3, 4, "layer group [name] label", &cli_set_label, STRING }; diff --git a/src/core/daemon.c b/src/core/daemon.c index ae5ba9c..8d39dbd 100644 --- a/src/core/daemon.c +++ b/src/core/daemon.c @@ -91,6 +91,28 @@ bool parse_list(BuxtonControlMessage msg, size_t count, BuxtonData *list, key->layer = list[0].store.d_string; key->group = list[1].store.d_string; break; + case BUXTON_CONTROL_GET_KEY_TYPE: + if (count == 4) { + if (list[0].type != STRING || list[1].type != STRING || + list[2].type != STRING || list[3].type != UINT32) { + return false; + } + key->layer = list[0].store.d_string; + key->group = list[1].store.d_string; + key->name = list[2].store.d_string; + key->type = list[3].store.d_uint32; + } else if (count == 3) { + if(list[0].type != STRING || list[1].type != STRING || + list[2].type != UINT32) { + return false; + } + key->group = list[0].store.d_string; + key->name = list[1].store.d_string; + key->type = list[2].store.d_uint32; + } else { + return false; + } + break; case BUXTON_CONTROL_GET: if (count == 4) { if (list[0].type != STRING || list[1].type != STRING || @@ -224,6 +246,9 @@ bool buxtond_handle_message(BuxtonDaemon *self, client_list_item *client, size_t case BUXTON_CONTROL_REMOVE_GROUP: remove_group(self, client, &key, &response); break; + case BUXTON_CONTROL_GET_KEY_TYPE: + data = get_key_type(self, client, &key, &response); + break; case BUXTON_CONTROL_GET: data = get_value(self, client, &key, &response); break; @@ -305,6 +330,21 @@ bool buxtond_handle_message(BuxtonDaemon *self, client_list_item *client, size_t abort(); } break; + case BUXTON_CONTROL_GET_KEY_TYPE: + if (data && !buxton_array_add(out_list, data)) { + abort(); + } + response_len = buxton_serialize_message(&response_store, + BUXTON_CONTROL_STATUS, + msgid, out_list); + if (response_len == 0) { + if (errno == ENOMEM) { + abort(); + } + buxton_log("Failed to serialize get_key_type response message\n"); + abort(); + } + break; case BUXTON_CONTROL_GET: if (data && !buxton_array_add(out_list, data)) { abort(); @@ -671,6 +711,54 @@ void unset_value(BuxtonDaemon *self, client_list_item *client, buxton_debug("Daemon unset value completed\n"); } +BuxtonData *get_key_type(BuxtonDaemon *self, client_list_item *client, + _BuxtonKey *key, int32_t *status) +{ + BuxtonData *data = NULL; + BuxtonString label; + int32_t ret; + + assert(self); + assert(client); + assert(key); + assert(status); + + *status = -1; + + data = malloc0(sizeof(BuxtonData)); + if (!data) { + abort(); + } + + if (key->layer.value) { + buxton_debug("Daemon getting [%s][%s][%s] \n", key->layer.value, + key->group.value, key->name.value); + } else { + buxton_debug("Daemon getting [%s][%s] \n", key->group.value, + key->name.value); + } + self->buxton.client.uid = client->cred.uid; + ret = buxton_direct_get_value(&self->buxton, key, data, &label, + client->smack_label); + + if (ret) { + goto fail; + } + + free(label.value); + buxton_debug("get key type returned successfully from db\n"); + + *status = 0; + goto end; + +fail: + buxton_debug("get key type failed\n"); + free(data); + data = NULL; +end: + return data; +} + BuxtonData *get_value(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key, int32_t *status) { diff --git a/src/core/daemon.h b/src/core/daemon.h index 4156f92..06537a2 100644 --- a/src/core/daemon.h +++ b/src/core/daemon.h @@ -145,6 +145,18 @@ void create_group(BuxtonDaemon *self, client_list_item *client, void remove_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key, int32_t *status); +/** + * Buxton daemon function for getting the type of a key + * @param self buxtond instance being run + * @param client Used to validate smack access + * @param key Key for the value being sought + * @param status Will be set with the int32_t result of the operation + * @returns BuxtonData Value stored for key if successful otherwise NULL + */ +BuxtonData *get_key_type(BuxtonDaemon *self, client_list_item *client, + _BuxtonKey *key, int32_t *status) + __attribute__((warn_unused_result)); + /** * Buxton daemon function for getting a value * @param self buxtond instance being run diff --git a/src/db/gdbm.c b/src/db/gdbm.c index e8988c4..20c1185 100644 --- a/src/db/gdbm.c +++ b/src/db/gdbm.c @@ -197,6 +197,81 @@ static int set_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data, return ret; } +static int get_key_type(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data, + BuxtonString *label) +{ + GDBM_FILE db; + datum key_data; + datum value; + uint8_t *data_store = NULL; + int ret; + uint32_t sz; + BuxtonData *temp_data; + + assert(layer); + + temp_data = malloc0(sizeof(BuxtonData)); + if (!temp_data) { + abort(); + } + + if (key->name.value) { + sz = key->group.length + key->name.length; + key_data.dptr = malloc(sz); + if (!key_data.dptr) { + abort(); + } + + /* size is string\0string\0 so just write, bonus for + nil seperator being added without extra work */ + key_data.dsize = (int)sz; + memcpy(key_data.dptr, key->group.value, key->group.length); + memcpy(key_data.dptr + key->group.length, key->name.value, + key->name.length); + } else { + key_data.dptr = malloc(key->group.length); + if (!key_data.dptr) { + abort(); + } + + memcpy(key_data.dptr, key->group.value, key->group.length); + key_data.dsize = (int)key->group.length; + } + + memzero(&value, sizeof(datum)); + db = db_for_resource(layer); + if (!db) { + /* + * Set negative here to indicate layer not found + * rather than key not found, optimization for + * set value + */ + ret = -ENOENT; + goto end; + } + + value = gdbm_fetch(db, key_data); + if (value.dsize <0 || value.dptr == NULL) { + ret = ENOENT; + goto end; + } + + data_store = (uint8_t*)value.dptr; + buxton_deserialize(data_store, temp_data, label); + + data->type = UINT32; + data->store.d_uint32 = temp_data->type; + ret = 0; + +end: + free(key_data.dptr); + free(value.dptr); + free(temp_data); + data_store = NULL; + + return ret; +} + static int get_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data, BuxtonString *label) { @@ -425,6 +500,7 @@ _bx_export_ bool buxton_module_init(BuxtonBackend *backend) /* Point the struct methods back to our own */ backend->set_value = &set_value; + backend->get_key_type = &get_key_type; backend->get_value = &get_value; backend->list_keys = &list_keys; backend->unset_value = &unset_value; diff --git a/src/db/memory.c b/src/db/memory.c index d763390..60fd428 100644 --- a/src/db/memory.c +++ b/src/db/memory.c @@ -166,6 +166,67 @@ static int set_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data, return ret; } + +static int get_key_type(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data, + BuxtonString *label) +{ + printf("in memory.c, get_key_type\n"); + Hashmap *db; + BuxtonArray *stored; + BuxtonData *d; + BuxtonString *l; + char *full_key = NULL; + int ret; + + assert(layer); + assert(key); + assert(label); + assert(data); + + db = _db_for_resource(layer); + if (!db) { + /* + * Set negative here to indicate layer not found + * rather than key not found, optimization for + * set value + */ + ret = -ENOENT; + goto end; + } + + if (key->name.value) { + if (asprintf(&full_key, "%s%s", key->group.value, key->name.value) == -1) { + abort(); + } + } else { + full_key = strdup(key->group.value); + if (!full_key) { + abort(); + } + } + + stored = (BuxtonArray *)hashmap_get(db, full_key); + if (!stored) { + ret = ENOENT; + goto end; + } + d = buxton_array_get(stored, 0); + + data->type = UINT32; + data->store.d_uint32 = d->type; + + l = buxton_array_get(stored, 1); + if (!buxton_string_copy(l, label)) { + abort(); + } + + ret = 0; + +end: + free(full_key); + return ret; +} + static int get_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data, BuxtonString *label) { @@ -321,6 +382,7 @@ _bx_export_ bool buxton_module_init(BuxtonBackend *backend) /* Point the struct methods back to our own */ backend->set_value = &set_value; + backend->get_key_type = &get_key_type; backend->get_value = &get_value; backend->unset_value = &unset_value; backend->list_keys = NULL; diff --git a/src/include/buxton.h b/src/include/buxton.h index 8cff5f7..8ffab3e 100644 --- a/src/include/buxton.h +++ b/src/include/buxton.h @@ -53,6 +53,7 @@ typedef enum BuxtonDataType { FLOAT, /**group.value) || !(k->name.value) || k->type != UNKNOWN) { + return EINVAL; + } + + r = buxton_wire_get_key_type((_BuxtonClient *)client, k, callback, data); + if (!r) { + return -1; + } + + if (sync) { + ret = buxton_wire_get_response(client); + if (ret <= 0) { + ret = -1; + } else { + ret = 0; + } + } + + return ret; +} + int buxton_get_value(BuxtonClient client, BuxtonKey key, BuxtonCallback callback, @@ -151,7 +182,8 @@ int buxton_get_value(BuxtonClient client, _BuxtonKey *k = (_BuxtonKey *)key; if (!k || !(k->group.value) || !(k->name.value) || - k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) { + k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX || + k->type == UNKNOWN) { return EINVAL; } @@ -183,7 +215,8 @@ int buxton_register_notification(BuxtonClient client, _BuxtonKey *k = (_BuxtonKey *)key; if (!k || !k->group.value || !k->name.value || - k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) { + k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX || + k->type == UNKNOWN) { return EINVAL; } @@ -216,7 +249,8 @@ int buxton_unregister_notification(BuxtonClient client, _BuxtonKey *k = (_BuxtonKey *)key; if (!k || !k->group.value || !k->name.value || - k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) { + k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX || + k->type == UNKNOWN) { return EINVAL; } @@ -250,7 +284,8 @@ int buxton_set_value(BuxtonClient client, _BuxtonKey *k = (_BuxtonKey *)key; if (!k || !k->group.value || !k->name.value || !k->layer.value || - k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX || !value) { + k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX || + k->type == UNKNOWN || !value) { return EINVAL; } @@ -419,7 +454,8 @@ int buxton_unset_value(BuxtonClient client, _BuxtonKey *k = (_BuxtonKey *)key; if (!k || !k->group.value || !k->name.value || !k->layer.value || - k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX) { + k->type <= BUXTON_TYPE_MIN || k->type >= BUXTON_TYPE_MAX || + k->type == UNKNOWN) { return EINVAL; } @@ -654,7 +690,7 @@ void *buxton_response_value(BuxtonResponse response) } type = buxton_response_type(response); - if (type == BUXTON_CONTROL_GET) { + if (type == BUXTON_CONTROL_GET || type == BUXTON_CONTROL_GET_KEY_TYPE) { d = buxton_array_get(r->data, 1); } else if (type == BUXTON_CONTROL_CHANGED) { if (r->data->len) { diff --git a/src/libbuxton/lbuxton.sym b/src/libbuxton/lbuxton.sym index 37c36d0..a1ce148 100644 --- a/src/libbuxton/lbuxton.sym +++ b/src/libbuxton/lbuxton.sym @@ -7,6 +7,7 @@ BUXTON_1 { buxton_set_label; buxton_create_group; buxton_remove_group; + buxton_get_key_type; buxton_get_value; buxton_unset_value; buxton_register_notification; diff --git a/src/shared/backend.c b/src/shared/backend.c index 29eb8f6..8e63c5b 100644 --- a/src/shared/backend.c +++ b/src/shared/backend.c @@ -249,6 +249,7 @@ void destroy_backend(BuxtonBackend *backend) assert(backend); backend->set_value = NULL; + backend->get_key_type = NULL; backend->get_value = NULL; backend->list_keys = NULL; backend->unset_value = NULL; diff --git a/src/shared/backend.h b/src/shared/backend.h index edcde53..ee3c1af 100644 --- a/src/shared/backend.h +++ b/src/shared/backend.h @@ -104,6 +104,7 @@ typedef struct BuxtonBackend { void *module; /**get_value(layer, key, data, data_label); + if (key->type == UNKNOWN) { + ret = backend->get_key_type(layer, key, data, data_label); + } else { + ret = backend->get_value(layer, key, data, data_label); + } + if (!ret) { /* Access checks are not needed for direct clients, where client_label is NULL */ if (data_label->value && client_label && client_label->value && diff --git a/src/shared/direct.h b/src/shared/direct.h index 00b5ee7..8e01152 100644 --- a/src/shared/direct.h +++ b/src/shared/direct.h @@ -99,6 +99,38 @@ bool buxton_direct_set_value(BuxtonControl *control, BuxtonString *label) __attribute__((warn_unused_result)); +/** + * Retrieve a key type from Buxton + * @param control An initialized control structure + * @param key The key to retrieve + * @param data An empty BuxtonData, where data is stored + * @param data_label The Smack label of the data + * @param client_label The Smack label of the client + * @return A int32_t value, indicating success of the operation + */ +int32_t buxton_direct_get_key_type(BuxtonControl *control, + _BuxtonKey *key, + BuxtonData *data, + BuxtonString *data_label, + BuxtonString *client_label) + __attribute__((warn_unused_result)); + +/** + * Retrieve a key type from Buxton by layer + * @param control An initialized control structure + * @param key The key to retrieve + * @param data An empty BuxtonData, where type is stored + * @param data_label The Smack label of the data + * @param client_label The Smack label of the client + * @return An int value, indicating success of the operation + */ +int buxton_direct_get_key_type_for_layer(BuxtonControl *control, + _BuxtonKey *key, + BuxtonData *data, + BuxtonString *data_label, + BuxtonString *client_label) + __attribute__((warn_unused_result)); + /** * Retrieve a value from Buxton * @param control An initialized control structure diff --git a/src/shared/protocol.c b/src/shared/protocol.c index 3842399..2dbfa84 100644 --- a/src/shared/protocol.c +++ b/src/shared/protocol.c @@ -664,6 +664,64 @@ bool buxton_wire_remove_group(_BuxtonClient *client, _BuxtonKey *key, return ret; } +bool buxton_wire_get_key_type(_BuxtonClient *client, _BuxtonKey *key, + BuxtonCallback callback, void *data) +{ + bool ret = false; + size_t send_len = 0; + _cleanup_free_ uint8_t *send = NULL; + BuxtonArray *list = NULL; + BuxtonData d_layer; + BuxtonData d_group; + BuxtonData d_name; + BuxtonData d_type; //do i need this? + uint32_t msgid = get_msgid(); + + buxton_string_to_data(&key->group, &d_group); + buxton_string_to_data(&key->name, &d_name); + d_type.type = UINT32; // do i need this? + d_type.store.d_int32 = key->type; // do i need this? + + list = buxton_array_new(); + if (key->layer.value) { + buxton_string_to_data(&key->layer, &d_layer); + if (!buxton_array_add(list, &d_layer)) { + buxton_log("Unable to prepare get_key_type message\n"); + goto end; + } + } + if (!buxton_array_add(list, &d_group)) { + buxton_log("Failed to add group to get_key_type array\n"); + goto end; + } + if (!buxton_array_add(list, &d_name)) { + buxton_log("Failed to add name to get_key_type array\n"); + goto end; + } + if (!buxton_array_add(list, &d_type)) { + buxton_log("Failed to add type to get_key_type_array\n"); + goto end; + } + + send_len = buxton_serialize_message(&send, BUXTON_CONTROL_GET_KEY_TYPE, + msgid, list); + + if (send_len == 0) { + goto end; + } + + if (!send_message(client, send, send_len, callback, data, msgid, + BUXTON_CONTROL_GET_KEY_TYPE, key)) { + goto end; + } + + ret = true; + +end: + buxton_array_free(&list, NULL); + return ret; +} + bool buxton_wire_get_value(_BuxtonClient *client, _BuxtonKey *key, BuxtonCallback callback, void *data) { diff --git a/src/shared/protocol.h b/src/shared/protocol.h index 1e2aea6..50151ed 100644 --- a/src/shared/protocol.h +++ b/src/shared/protocol.h @@ -160,6 +160,18 @@ bool buxton_wire_remove_group(_BuxtonClient *client, _BuxtonKey *key, BuxtonCallback callback, void *data) __attribute__((warn_unused_result)); +/** + * Send a GET_KEY_TYPE message over the wire protocol, return the type of the key + * @param client Client connection + * @param key _BuxtonKey pointer + * @param callback A callback function to handle daemon reply + * @param data User data to be used with callback functionb + * @return a boolean value, indicating success of the operation + */ +bool buxton_wire_get_key_type(_BuxtonClient *client, _BuxtonKey *key, + BuxtonCallback callback, void *data) + __attribute__((warn_unused_result)); + /** * Send a GET message over the wire protocol, return the data * @param client Client connection diff --git a/test/check_buxton.c b/test/check_buxton.c index 0033899..d5c56cc 100644 --- a/test/check_buxton.c +++ b/test/check_buxton.c @@ -142,6 +142,32 @@ START_TEST(buxton_direct_set_value_check) } END_TEST +START_TEST(buxton_direct_get_key_type_for_layer_check) +{ + BuxtonControl c; + BuxtonData result; + BuxtonString dlabel; + _BuxtonKey key; + + key.layer = buxton_string_pack("test-gdbm"); + key.group = buxton_string_pack("bxt_test_group"); + key.name = buxton_string_pack("bxt_test_key"); + key.type = UNKNOWN; + + c.client.uid = getuid(); + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + fail_if(buxton_direct_get_value_for_layer(&c, &key, &result, &dlabel, NULL), + "Retrieving key type from buxton gdbm backend failed."); + fail_if(result.type != UINT32, + "Buxton gdbm backend returned incorrect result type."); + //FIXME: get label test figured out + fail_if(result.store.d_uint32 != (uint32_t)STRING, + "Buxton gdbm returned a different key type to that set."); + buxton_direct_close(&c); +} +END_TEST + START_TEST(buxton_direct_get_value_for_layer_check) { BuxtonControl c; @@ -170,6 +196,40 @@ START_TEST(buxton_direct_get_value_for_layer_check) } END_TEST +START_TEST(buxton_direct_get_key_type_check) +{ + BuxtonControl c; + BuxtonData data, result; + BuxtonString dlabel; + _BuxtonKey key; + key.layer = buxton_string_pack("test-gdbm"); + key.group = buxton_string_pack("bxt_test_group"); + key.name = buxton_string_pack("bxt_test_key"); + key.type = STRING; + + fail_if(buxton_direct_open(&c) == false, + "Direct open failed without daemon."); + + c.client.uid = getuid(); + data.type = STRING; + data.store.d_string = buxton_string_pack("bxt_test_value2"); + fail_if(data.store.d_string.value == NULL, + "Failed to allocate test string."); + fail_if(buxton_direct_set_value(&c, &key, &data, NULL) == false, + "Failed to set second value."); + + key.type = UNKNOWN; + fail_if(buxton_direct_get_value(&c, &key, &result, &dlabel, NULL) == -1, + "Retrieving key type from buxton gdbm backend failed."); + fail_if(result.type != UINT32, + "Buxton gdbm backend returned incorrect result type."); + //FIXME: figure out label check + fail_if(result.store.d_uint32 != (uint32_t)data.type, + "Buxton gdbm returned a different key type to that set."); + buxton_direct_close(&c); +} +END_TEST + START_TEST(buxton_direct_get_value_check) { BuxtonControl c; @@ -918,6 +978,90 @@ START_TEST(buxton_wire_set_label_check) } END_TEST +START_TEST(buxton_wire_get_key_type_check) +{ + _BuxtonClient client; + int server; + ssize_t size; + BuxtonData *list = NULL; + uint8_t buf[4096]; + ssize_t r; + _BuxtonKey key; + BuxtonControlMessage msg; + uint32_t msgid; + + setup_socket_pair(&(client.fd), &server); + fail_if(fcntl(client.fd, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + fail_if(fcntl(server, F_SETFL, O_NONBLOCK), + "Failed to set socket to non blocking"); + + fail_if(!setup_callbacks(), + "Failed to initialeze callbacks"); + + key.layer = buxton_string_pack("layer"); + key.group = buxton_string_pack("group"); + key.name = buxton_string_pack("name"); + key.type = UNKNOWN; + fail_if(buxton_wire_get_key_type(&client, &key, NULL, + NULL) != true, + "Failed to properly get value 1"); + + r = read(server, buf, 4096); + fail_if(r < 0, "Read from client failed 1"); + size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list); + fail_if(size != 4, "Failed to get valid message from buffer 1"); + fail_if(msg != BUXTON_CONTROL_GET_KEY_TYPE, + "Failed to get correct control type 1"); + fail_if(list[0].type != STRING, "Failed to set correct layer type 1"); + fail_if(list[1].type != STRING, "Failed to set correct group type 1"); + fail_if(list[2].type != STRING, "Failed to set correct name type 1"); + fail_if(list[3].type != UINT32, "Failed to set correct type type 1"); + fail_if(!streq(list[0].store.d_string.value, "layer"), + "Failed to set correct layer 1"); + fail_if(!streq(list[1].store.d_string.value, "group"), + "Failed to set correct group 1"); + fail_if(!streq(list[2].store.d_string.value, "name"), + "Failed to set correct name 1"); + fail_if(list[3].store.d_uint32 != UNKNOWN, + "Failed to set correct type 1"); + + free(list[0].store.d_string.value); + free(list[1].store.d_string.value); + free(list[2].store.d_string.value); + free(list); + + key.layer.value = NULL; + fail_if(buxton_wire_get_key_type(&client, &key, NULL, + NULL) != true, + "Failed to properly get value 2"); + + r = read(server, buf, 4096); + fail_if(r < 0, "Read from client failed 2"); + size = buxton_deserialize_message(buf, &msg, (size_t)r, &msgid, &list); + fail_if(size != 3, "Failed to get valid message from buffer 2"); + fail_if(msg != BUXTON_CONTROL_GET_KEY_TYPE, + "Failed to get correct control type 2"); + fail_if(list[0].type != STRING, "Failed to set correct group type 2"); + fail_if(list[1].type != STRING, "Failed to set correct name type 2"); + fail_if(list[2].type != UINT32, "Failed to set correct type type 2"); + fail_if(!streq(list[0].store.d_string.value, "group"), + "Failed to set correct group 2"); + fail_if(!streq(list[1].store.d_string.value, "name"), + "Failed to set correct name 2"); + fail_if(list[2].store.d_uint32 != UNKNOWN, + "Failed to set correct type 2"); + + free(list[0].store.d_string.value); + free(list[1].store.d_string.value); + free(list); + + cleanup_callbacks(); + close(client.fd); + close(server); +} +END_TEST + START_TEST(buxton_wire_get_value_check) { _BuxtonClient client; @@ -1177,7 +1321,9 @@ buxton_suite(void) tcase_add_test(tc, buxton_direct_create_group_check); tcase_add_test(tc, buxton_direct_remove_group_check); tcase_add_test(tc, buxton_direct_set_value_check); + tcase_add_test(tc, buxton_direct_get_key_type_for_layer_check); tcase_add_test(tc, buxton_direct_get_value_for_layer_check); + tcase_add_test(tc, buxton_direct_get_key_type_check); tcase_add_test(tc, buxton_direct_get_value_check); tcase_add_test(tc, buxton_memory_backend_check); tcase_add_test(tc, buxton_key_check); @@ -1194,6 +1340,7 @@ buxton_suite(void) tcase_add_test(tc, buxton_wire_get_response_check); tcase_add_test(tc, buxton_wire_set_value_check); tcase_add_test(tc, buxton_wire_set_label_check); + tcase_add_test(tc, buxton_wire_get_key_type_check); tcase_add_test(tc, buxton_wire_get_value_check); tcase_add_test(tc, buxton_wire_unset_value_check); tcase_add_test(tc, buxton_wire_create_group_check); diff --git a/test/check_daemon.c b/test/check_daemon.c index 90775a8..ccf5575 100644 --- a/test/check_daemon.c +++ b/test/check_daemon.c @@ -372,6 +372,79 @@ START_TEST(buxton_set_label_check) } END_TEST +static void client_get_key_type_test(BuxtonResponse response, void *data) +{ + BuxtonKey key; + char *group; + char *name; + BuxtonDataType *t; + BuxtonDataType *type = (BuxtonDataType *)data; + + fail_if((buxton_response_status(response) != 0), + "Get key type failed"); + + key = buxton_response_key(response); + fail_if(!key, "Failed to get key"); + group = buxton_key_get_group(key); + fail_if(!group, "Failed to get group"); + fail_if(!streq(group, "group"), + "Failed to get correct group"); + name = buxton_key_get_name(key); + fail_if(!name, "Failed to get name"); + fail_if(!streq(name, "name"), + "Failed to get correct name"); + t = buxton_response_value(response); + fail_if(!t, "Failed to get type"); + printf("type = %d\n", *t); + fail_if(!(*t = *type), "Failed to get correct type"); + + free(group); + free(name); + buxton_key_free(key); +} +START_TEST(buxton_get_key_type_for_layer_check) +{ + BuxtonClient c = NULL; + BuxtonDataType type = STRING; + BuxtonKey key = buxton_key_create("group", "name", "test-gdbm-user", UNKNOWN); + + fail_if(buxton_open(&c) == -1, + "Open failed with daemon."); + fail_if(buxton_get_key_type(c, key, client_get_key_type_test, + &type, true), + "Retrieving key type from buxton gdbm backend failed."); +} +END_TEST + +START_TEST(buxton_get_key_type_check) +{ + BuxtonClient c = NULL; + BuxtonDataType type = STRING; + + BuxtonKey group = buxton_key_create("group", NULL, "test-gdbm", STRING); + fail_if(!group, "Failed to create key for group"); + BuxtonKey key = buxton_key_create("group", "name", "test-gdbm", STRING); + + fail_if(buxton_open(&c) == -1, + "Open failed with daemon."); + + fail_if(buxton_create_group(c, group, NULL, NULL, true), + "Creating group in buxton failed."); + fail_if(buxton_set_label(c, group, "*", NULL, NULL, true), + "Setting group in buxton failed."); + fail_if(buxton_set_value(c, key, "bxt_test_value2", + client_set_value_test, "group", true), + "Failed to set second value."); + buxton_key_free(group); + buxton_key_free(key); + key = buxton_key_create("group", "name", NULL, UNKNOWN); + fail_if(buxton_get_key_type(c, key, client_get_key_type_test, + &type, true), + "Retrieving type from buxton gdbm backend failed."); + buxton_key_free(key); +} +END_TEST + static void client_get_value_test(BuxtonResponse response, void *data) { BuxtonKey key; @@ -519,6 +592,78 @@ START_TEST(parse_list_check) fail_if(key.type != l1[2].store.d_uint32, "Failed to set correct unnotify type"); + fail_if(parse_list(BUXTON_CONTROL_GET_KEY_TYPE, 5, l2, &key, &value), + "Parsed bad get_key_type argument count"); + l2[0].type = INT32; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_GET_KEY_TYPE, 4, l2, &key, &value), + "Parsed bad get_key_type type 1"); + l2[0].type = STRING; + l2[1].type = FLOAT; + l2[2].type = STRING; + l2[3].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_GET_KEY_TYPE, 4, l2, &key, &value), + "Parsed bad get_key_type type 2"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = BOOLEAN; + l2[3].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_GET_KEY_TYPE, 4, l2, &key, &value), + "Parsed bad get_key_type type 3"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = STRING; + fail_if(parse_list(BUXTON_CONTROL_GET_KEY_TYPE, 4, l2, &key, &value), + "Parsed bad get_key_type type 4"); + l2[0].type = STRING; + l2[1].type = STRING; + l2[2].type = STRING; + l2[3].type = UINT32; + l2[0].store.d_string = buxton_string_pack("s5"); + l2[1].store.d_string = buxton_string_pack("s6"); + l2[2].store.d_string = buxton_string_pack("s7"); + l2[3].store.d_uint32 = UNKNOWN; + fail_if(!parse_list(BUXTON_CONTROL_GET_KEY_TYPE, 4, l2, &key, &value), + "Unable to parse valid get_key_type 1"); + fail_if(!streq(key.layer.value, l2[0].store.d_string.value), + "Failed to set correct get_key_type layer 1"); + fail_if(!streq(key.group.value, l2[1].store.d_string.value), + "Failed to set correct get_key_type group 1"); + fail_if(!streq(key.name.value, l2[2].store.d_string.value), + "Failed to set correct get_key_type name"); + fail_if(key.type != l2[3].store.d_uint32, + "Failed to set correct get_key_type type 1"); + l2[0].store.d_string = buxton_string_pack("s6"); + l2[1].store.d_string = buxton_string_pack("s6"); + l2[2].type = UINT32; + l2[2].store.d_uint32 = UNKNOWN; + fail_if(!parse_list(BUXTON_CONTROL_GET_KEY_TYPE, 3, l2, &key, &value), + "Unable to parse valid get_key_type 2"); + fail_if(!streq(key.group.value, l2[0].store.d_string.value), + "Failed to set correct get_key_type group 2"); + fail_if(!streq(key.name.value, l2[1].store.d_string.value), + "Failed to set correct get_key_type name 2"); + fail_if(key.type != l2[2].store.d_uint32, + "Failed to set correct get_key_type type 2"); + l1[0].type = INT32; + l1[1].type = STRING; + l1[2].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_GET_KEY_TYPE, 3, l1, &key, &value), + "Parsed bad get_key_type type 5"); + l1[0].type = STRING; + l1[1].type = FLOAT; + l1[2].type = UINT32; + fail_if(parse_list(BUXTON_CONTROL_GET_KEY_TYPE, 3, l1, &key, &value), + "Parsed bad get_key_type type 6"); + l1[0].type = STRING; + l1[1].type = STRING; + l1[2].type = BOOLEAN; + fail_if(parse_list(BUXTON_CONTROL_GET_KEY_TYPE, 3, l1, &key, &value), + "Parsed bad get_key_type type 7"); + fail_if(parse_list(BUXTON_CONTROL_GET, 5, l2, &key, &value), "Parsed bad get argument count"); l2[0].type = INT32; @@ -934,6 +1079,54 @@ START_TEST(set_value_check) } END_TEST +START_TEST(get_key_type_check) +{ + _BuxtonKey key = { {0}, {0}, {0}, 0}; + BuxtonData *value; + client_list_item client; + int32_t status; + BuxtonDaemon server; + BuxtonString clabel = buxton_string_pack("_"); + + fail_if(!buxton_direct_open(&server.buxton), + "Failed to open buxton direct connection"); + + fail_if(!buxton_cache_smack_rules(), + "Failed to cache smack rules"); + client.cred.uid = getuid(); + if (use_smack()) + client.smack_label = &clabel; + else + client.smack_label = NULL; + server.buxton.client.uid = 0; + key.layer = buxton_string_pack("test-gdbm-user"); + key.group = buxton_string_pack("daemon-check"); + key.name = buxton_string_pack("name"); + key.type = UNKNOWN; + + value = get_key_type(&server, &client, &key, &status); + fail_if(!value, "Failed to get value"); + fail_if(status != 0, "Failed to get value"); + fail_if(value->type != UINT32, "Failed to get correct type"); + fail_if((value->store.d_uint32 != (uint32_t)STRING), "Failed to get correct value"); + fail_if(server.buxton.client.uid != client.cred.uid, "Failed to change buxton uid"); + free(value); + + server.buxton.client.uid = 0; + key.layer.value = NULL; + key.layer.length = 0; + value = get_key_type(&server, &client, &key, &status); + fail_if(!value, "Failed to get value 2"); + fail_if(status != 0, "Failed to get value 2"); + fail_if(value->type != UINT32, "Failed to get correct type 2"); + fail_if((value->store.d_uint32 != (uint32_t)STRING), "Failed to get correct value 2"); + fail_if(server.buxton.client.uid != client.cred.uid, "Failed to change buxton uid 2"); + free(value); + + buxton_direct_close(&server.buxton); +} +END_TEST + START_TEST(get_value_check) { _BuxtonKey key = { {0}, {0}, {0}, 0}; @@ -1462,6 +1655,118 @@ START_TEST(buxtond_handle_message_set_value_check) } END_TEST +START_TEST(buxtond_handle_message_get_key_type_check) +{ + int client, server; + BuxtonDaemon daemon; + BuxtonString slabel; + size_t size; + BuxtonData data1, data2, data3, data4; + client_list_item cl; + bool r; + BuxtonData *list; + BuxtonArray *out_list; + BuxtonArray *out_list2; + BuxtonControlMessage msg; + ssize_t csize; + ssize_t s; + uint8_t buf[4096]; + uint32_t msgid; + + setup_socket_pair(&client, &server); + out_list = buxton_array_new(); + fail_if(!out_list, "Failed to allocate list"); + + cl.fd = server; + slabel = buxton_string_pack("_"); + if (use_smack()) + cl.smack_label = &slabel; + else + cl.smack_label = NULL; + cl.cred.uid = getuid(); + daemon.buxton.client.uid = 1001; + fail_if(!buxton_cache_smack_rules(), "Failed to cache Smack rules"); + fail_if(!buxton_direct_open(&daemon.buxton), + "Failed to open buxton direct connection"); + + data1.type = STRING; + data1.store.d_string = buxton_string_pack("test-gdbm-user"); + data2.type = STRING; + data2.store.d_string = buxton_string_pack("daemon-check"); + data3.type = STRING; + data3.store.d_string = buxton_string_pack("name"); + data4.type = UINT32; + data4.store.d_uint32 = UNKNOWN; + r = buxton_array_add(out_list, &data1); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data2); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data3); + fail_if(!r, "Failed to add element to array"); + r = buxton_array_add(out_list, &data4); + fail_if(!r, "Failed to add element to array"); + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_GET_KEY_TYPE, 0, + out_list); + fail_if(size == 0, "Failed to serialize message"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to get message 1"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 2, "Failed to get valid message from buffer"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type"); + fail_if(msgid != 0, "Failed to get correct message id"); + fail_if(list[0].type != INT32, "Failed to get correct response type"); + fail_if(list[0].store.d_int32 != 0, + "Failed to get value"); + fail_if(list[1].type != UINT32, "Failed to get correct value type"); + fail_if(list[1].store.d_uint32 != (uint32_t)STRING, + "Failed to get correct value"); + + free(list[1].store.d_string.value); + free(list); + + out_list2 = buxton_array_new(); + fail_if(!out_list2, "Failed to allocate list 2"); + r = buxton_array_add(out_list2, &data2); + fail_if(!r, "Failed to add element to array 2"); + r = buxton_array_add(out_list2, &data3); + fail_if(!r, "Failed to add element to array 2"); + r = buxton_array_add(out_list2, &data4); + fail_if(!r, "Failed to add element to array 2"); + size = buxton_serialize_message(&cl.data, BUXTON_CONTROL_GET_KEY_TYPE, 0, + out_list2); + fail_if(size == 0, "Failed to serialize message 2"); + r = buxtond_handle_message(&daemon, &cl, size); + free(cl.data); + fail_if(!r, "Failed to get message 2"); + + s = read(client, buf, 4096); + fail_if(s < 0, "Read from client failed 2"); + csize = buxton_deserialize_message(buf, &msg, (size_t)s, &msgid, &list); + fail_if(csize != 2, "Failed to get correct response to get 2"); + fail_if(msg != BUXTON_CONTROL_STATUS, + "Failed to get correct control type 2"); + fail_if(msgid != 0, "Failed to get correct message id 2"); + fail_if(list[0].type != INT32, "Failed to get correct response type 2"); + fail_if(list[0].store.d_int32 != 0, + "Failed to get value 2"); + fail_if(list[1].type != UINT32, "Failed to get correct value type 2"); + fail_if(list[1].store.d_uint32 != (uint32_t)STRING, + "Failed to get correct value 2"); + + free(list[1].store.d_string.value); + free(list); + close(client); + buxton_direct_close(&daemon.buxton); + buxton_array_free(&out_list, NULL); + buxton_array_free(&out_list2, NULL); +} +END_TEST + START_TEST(buxtond_handle_message_get_check) { int client, server; @@ -2739,6 +3044,8 @@ daemon_suite(void) tcase_add_test(tc, buxton_remove_group_check); tcase_add_test(tc, buxton_set_value_check); tcase_add_test(tc, buxton_set_label_check); + tcase_add_test(tc, buxton_get_key_type_for_layer_check); + tcase_add_test(tc, buxton_get_key_type_check); tcase_add_test(tc, buxton_get_value_for_layer_check); tcase_add_test(tc, buxton_get_value_check); suite_add_tcase(s, tc); @@ -2750,6 +3057,7 @@ daemon_suite(void) tcase_add_test(tc, set_label_check); tcase_add_test(tc, set_value_check); + tcase_add_test(tc, get_key_type_check); tcase_add_test(tc, get_value_check); tcase_add_test(tc, register_notification_check); tcase_add_test(tc, buxtond_handle_message_error_check); @@ -2757,6 +3065,7 @@ daemon_suite(void) tcase_add_test(tc, buxtond_handle_message_remove_group_check); tcase_add_test(tc, buxtond_handle_message_set_label_check); tcase_add_test(tc, buxtond_handle_message_set_value_check); + tcase_add_test(tc, buxtond_handle_message_get_key_type_check); tcase_add_test(tc, buxtond_handle_message_get_check); tcase_add_test(tc, buxtond_handle_message_notify_check); tcase_add_test(tc, buxtond_handle_message_unset_check); From 7287b3b083182a07b51ee0b14d24a4506f48da0d Mon Sep 17 00:00:00 2001 From: Ceara Chewning Date: Wed, 3 Sep 2014 11:58:57 -0700 Subject: [PATCH 2/2] Implemented simple notification for buxton keys A buxton client connection is left open in the simple library as long as there is at least one notification registered with the daemon. Calls to simple register take a key name and a callback function. These, along with the previously set group and layer, are used to get a key type and register a notification in the daemon. A provided callback deals with the BuxtonResponse and calls the user's callback with the new data as a void * and the key name as a char *. sbuxton_get_fd and sbuxton_handle_response are provided to use in polling or a mainloop. Includes documentation for all new public functions and passing checks for new internal functions. --- Makefile.am | 16 +- demo/hellonotifysimple.c | 111 +++++++++ docs/buxton_client_handle_response.3 | 2 +- docs/buxtonsimple-api.7 | 9 + docs/sbuxton_get_fd.3 | 67 ++++++ docs/sbuxton_handle_response.3 | 64 +++++ docs/sbuxton_register_notify.3 | 162 +++++++++++++ docs/sbuxton_unregister_notify.3 | 1 + src/include/buxtonsimple.h | 33 +++ src/libbuxtonsimple/lbuxtonsimple.c | 333 ++++++++++++++++++++------ src/libbuxtonsimple/lbuxtonsimple.sym | 4 + src/libbuxtonsimple/simple_test.c | 222 +++++++++++++++++ src/shared/buxtonsimple-internals.c | 139 +++++++++++ src/shared/buxtonsimple-internals.h | 30 +++ test/check_buxtonsimple.c | 102 ++++++++ 15 files changed, 1220 insertions(+), 75 deletions(-) create mode 100644 demo/hellonotifysimple.c create mode 100644 docs/sbuxton_get_fd.3 create mode 100644 docs/sbuxton_handle_response.3 create mode 100644 docs/sbuxton_register_notify.3 create mode 100644 docs/sbuxton_unregister_notify.3 create mode 100644 src/libbuxtonsimple/simple_test.c diff --git a/Makefile.am b/Makefile.am index 28a6fcf..b6eca07 100644 --- a/Makefile.am +++ b/Makefile.am @@ -104,8 +104,10 @@ dist_man_MANS = \ docs/sbuxton_get_string.3 \ docs/sbuxton_get_int64.3 \ docs/sbuxton_get_uint64.3 \ + docs/sbuxton_get_fd.3 \ docs/sbuxton_get_float.3 \ docs/sbuxton_get_double.3 \ + docs/sbuxton_handle_response.3 \ docs/sbuxton_get_bool.3 \ docs/sbuxton_set_int32.3 \ docs/sbuxton_set_uint32.3 \ @@ -116,7 +118,9 @@ dist_man_MANS = \ docs/sbuxton_set_double.3 \ docs/sbuxton_set_bool.3 \ docs/sbuxton_set_group.3 \ - docs/sbuxton_remove_group.3 + docs/sbuxton_register_notify.3 \ + docs/sbuxton_remove_group.3 \ + docs/sbuxton_unregister_notify.3 endif TESTS = \ @@ -458,7 +462,8 @@ bin_PROGRAMS += \ bxt_hello_unset \ bxt_hello_notify \ bxt_hello_notify_multi \ - bxt_hello_simple + bxt_hello_simple \ + bxt_hello_notify_simple # Timing test bxt_timing_SOURCES = \ @@ -538,6 +543,13 @@ bxt_hello_simple_CFLAGS = \ bxt_hello_simple_LDADD = \ libbuxtonsimple.la +bxt_hello_notify_simple_SOURCES = \ + demo/hellonotifysimple.c +bxt_hello_notify_simple_CFLAGS = \ + $(AM_CFLAGS) +bxt_hello_notify_simple_LDADD = \ + libbuxtonsimple.la + if BUILD_GTK_DEMO bin_PROGRAMS += \ bxt_gtk_client diff --git a/demo/hellonotifysimple.c b/demo/hellonotifysimple.c new file mode 100644 index 0000000..de983bc --- /dev/null +++ b/demo/hellonotifysimple.c @@ -0,0 +1,111 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2014 Intel Corporation + * + * 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 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 "buxtonsimple.h" + +/* + * This demo creates a key "tk_i32" on group "tg_s5" and layer "user". + * It then registers for notification on that key in buxton, and registers + * for notifications on this instance of buxtonsimple in pollfd. Users can then + * changed the value of tk_i32 with the commandline interface (buxtonctl), which + * will trigger this callback, which prints the name of the key, and its new value. + */ + +/* Callback function for notifications */ +void tk_i32_notify_cb(void *key_data, char *key_name) +{ + int32_t *data = (int32_t *)key_data; + if (!data) { + printf("key %s was removed\n", key_name); + } else { + printf("key %s was changed to value %d\n", key_name, *data); + } +} + +int main(void) +{ + struct pollfd pfd[1]; + int r; + int fd; + int repoll_count = 10; + + /* Create group */ + errno = 0; + sbuxton_set_group("tg_s5", "user"); + printf("set_group: 'tg_s5', 'user', Error number: %s.\n", strerror(errno)); + + /* Test Int setting */ + int32_t i32 = (int32_t) rand() % 50 + 1; + printf("value should be set to %d.\n", i32); + errno = 0; + sbuxton_set_int32("tk_i32", i32); + printf("set_int32: 'tg_s5', 'tk_i32', Error number: %s.\n", strerror(errno)); + + /* Register for notifications in buxton */ + printf("Register for int32_t tk_i32\n"); + sbuxton_register_notify("tk_i32", &tk_i32_notify_cb); + + /* get fd */ + fd = sbuxton_get_fd(); + + +repoll: + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r < 0) { + printf("poll error\n"); + return -1; + } else if (r == 0) { + if (repoll_count-- > 0) { + goto out; + } + goto repoll; + } + + if (!sbuxton_handle_response()) { + printf("bad response from daemon\n"); + return -1; + } + + goto repoll; + +out: + /* unregister notifications */ + sbuxton_unregister_notify("tk_i32"); + + return 0; +} diff --git a/docs/buxton_client_handle_response.3 b/docs/buxton_client_handle_response.3 index c84aa89..ebf6264 100644 --- a/docs/buxton_client_handle_response.3 +++ b/docs/buxton_client_handle_response.3 @@ -25,7 +25,7 @@ buxton_client_handle_response \- Notification response helper .SH "SYNOPSIS" .nf \fB -#include +#include \fR .sp \fB diff --git a/docs/buxtonsimple-api.7 b/docs/buxtonsimple-api.7 index e4039b9..0984106 100644 --- a/docs/buxtonsimple-api.7 +++ b/docs/buxtonsimple-api.7 @@ -42,6 +42,15 @@ use these API functions\&. \(em Remove a group within a layer .br +.SS "Key notification functions +.PP +\fBsbuxton_register_notify\fR(3) +\(em Register a callback for a key notification +.br +\fBsbuxton_unregister_notify\fR(3) +\(em Unregister for a key notification +.br + .SS "Key set functions" .PP \fBsbuxton_set_int32\fR(3) diff --git a/docs/sbuxton_get_fd.3 b/docs/sbuxton_get_fd.3 new file mode 100644 index 0000000..03ea4fd --- /dev/null +++ b/docs/sbuxton_get_fd.3 @@ -0,0 +1,67 @@ +'\" t +.TH "SBUXTON_GET_FD" "3" "buxton 1" "sbuxton_get_fd" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +sbuxton_get_fd \- Get file descriptor for notifications + +.SH "SYNOPSIS" +.nf +\fB +#include +\fR +.sp +\fB +int sbuxton_get_fd(\fIvoid\fB) +\fR +.fi + +.SH "DESCRIPTION" +.PP +This function retrieves the file descriptor for the currently open +connection that \fBbuxtond\fR writes to. You must have registered +notifications to use this. This is use for polling or adding to a +mainloop to read notifications. + +For an example, see \fBsbuxton_register_notify\fR(3). + +.SH "RETURN VALUE" +.PP +Returns the file descriptor of the currently open connection or -1 +if there was an error.\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxtonsimple\-api\fR(7) +\fBsbuxton_register_notify\fR(3) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/sbuxton_handle_response.3 b/docs/sbuxton_handle_response.3 new file mode 100644 index 0000000..c00b144 --- /dev/null +++ b/docs/sbuxton_handle_response.3 @@ -0,0 +1,64 @@ +'\" t +.TH "SBUXTON_HANDLE_RESPONSE" "3" "buxton 1" "sbuxton_handle_response" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +sbuxton_handle_response \- Notification response helper + +.SH "SYNOPSIS" +.nf +\fB +#include +\fR +.sp +\fB +ssize_t sbuxton_handle_response(\fIvoid\fB) +\fR +.fi + +.SH "DESCRIPTION" +.PP +This function retrieves the response from \fBbuxtond\fR for the currently open +connection. You must have registered notifications to use this. + +For an example, see \fBsbuxton_register_notify\fR(3). + +.SH "RETURN VALUE" +.PP +Returns the number of messages processed, or -1 if there was an error\&. + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxtonsimple\-api\fR(7) +\fBsbuxton_register_notify\fR(3) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/sbuxton_register_notify.3 b/docs/sbuxton_register_notify.3 new file mode 100644 index 0000000..aa50bee --- /dev/null +++ b/docs/sbuxton_register_notify.3 @@ -0,0 +1,162 @@ +'\" t +.TH "SBUXTON_REGISTER_NOTIFY" "3" "buxton 1" "sbuxton_register_notify" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +sbuxton_register_notify, sbuxton_unregister_notify \- Manage key-name notifications + +.SH "SYNOPSIS" +.nf +\fB +#include +\fR +.sp +\fB +void sbuxton_register_notify(char *\fIname\fB, +.br + NotifyCallback \fIcallback\fB) +.sp +.br +void sbuxton_unregister_notify(char *\fIname\fB) +\fR +.fi + +.SH "DESCRIPTION" +.PP +These functions are used to manage key\-name notifications on +key \fIname\fR. + +To register for notifications on a specific key\-name, the client +should call \fBsbuxton_register_notify\fR(3)\&. Similarly, to +unregister for notifications, \fBsbuxton_unregister_notify\fR(3) +can be used\&. + +Both functions accept a callback function to register with +the daemon, referenced by the \fIcallback\fR argument; this +is called upon a change in the key\-name's value\&. The callback +function must take a void * and a char * and return void. + +.SH "CODE EXAMPLE" +.nf +.sp +#include +#include +#include +#include +#include +#include +#include + +#include "buxtonsimple.h" + +/* Callback function for notifications */ +void tk_i32_notify_cb(void *key_data, char *key_name) +{ + int32_t *data = (int32_t *)key_data; + if (!data) { + printf("key %s was removed\n", key_name); + } else { + printf("key %s was changed to value %d\n", key_name, *data); + } +} + +int main(void) +{ + struct pollfd pfd[1]; + int r; + int fd; + int repoll_count = 10; + + /* Create group */ + errno = 0; + sbuxton_set_group("tg_s5", "user"); + printf("set_group: 'tg_s5', 'user', Error number: %s.\n", strerror(errno)); + + /* Test Int setting */ + int32_t i32 = (int32_t) rand() % 50 + 1; + printf("value should be set to %d.\n", i32); + errno = 0; + sbuxton_set_int32("tk_i32", i32); + printf("set_int32: 'tg_s5', 'tk_i32', Error number: %s.\n", strerror(errno)); + + /* Register for notifications in buxton */ + printf("Register for int32_t tk_i32\n"); + sbuxton_register_notify("tk_i32", &tk_i32_notify_cb); + + /* get fd */ + fd = sbuxton_get_fd(); + + +repoll: + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + r = poll(pfd, 1, 5000); + + if (r < 0) { + printf("poll error\n"); + return -1; + } else if (r == 0) { + if (repoll_count-- > 0) { + goto out; + } + goto repoll; + } + + if (!sbuxton_handle_response()) { + printf("bad response from daemon\n"); + return -1; + } + + goto repoll; + +out: + /* unregister notifications */ + sbuxton_unregister_notify("tk_i32"); + + return 0; +} +.fi + +.SH "RETURN VALUE" +.PP +Returns void. On failure, errno is set to ENOTCONN if the client +couldn't connect, ENOMEM if memory allocation failed, or EACCES otherwise/ + +.SH "COPYRIGHT" +.PP +Copyright 2014 Intel Corporation\&. License: Creative Commons +Attribution\-ShareAlike 3.0 Unported\s-2\u[1]\d\s+2, with exception +for code examples found in the \fBCODE EXAMPLE\fR section, which are +licensed under the MIT license provided in the \fIdocs/LICENSE.MIT\fR +file from this buxton distribution\&. + +.SH "SEE ALSO" +.PP +\fBbuxton\fR(7), +\fBbuxtond\fR(8), +\fBbuxton\-api\fR(7) + +.SH "NOTES" +.IP " 1." 4 +Creative Commons Attribution\-ShareAlike 3.0 Unported +.RS 4 +\%http://creativecommons.org/licenses/by-sa/3.0/ +.RE diff --git a/docs/sbuxton_unregister_notify.3 b/docs/sbuxton_unregister_notify.3 new file mode 100644 index 0000000..2cf55cd --- /dev/null +++ b/docs/sbuxton_unregister_notify.3 @@ -0,0 +1 @@ +.so sbuxton_register_notify.3 diff --git a/src/include/buxtonsimple.h b/src/include/buxtonsimple.h index 96ef087..728da15 100644 --- a/src/include/buxtonsimple.h +++ b/src/include/buxtonsimple.h @@ -19,6 +19,7 @@ */ #include "buxton.h" +//#include "buxtonsimple-internals.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -30,8 +31,40 @@ # define _bx_export_ #endif +typedef void (*NotifyCallback)(void *, char*); + +typedef struct nstatus { + int status; + NotifyCallback callback; +} nstatus; /*Buxton Simple API Methods*/ + +/** + * Returns the client's file descriptor if there is a registered notification + * and the client connection is already open + * @ return An int representing the client's file descriptor, or -1 if not connected + */ +_bx_export_ int sbuxton_get_fd(void); +/** + * This wraps buxton_client_handle_response, which handles responses from the daemon + * It is used for handling notifications in the fd. It returns the number of messages + * it has handled, or -1 if the client is not connected + * @return An ssize_t + */ +_bx_export_ ssize_t sbuxton_handle_response(void); +/** + * Registers for notifications for the key name key. When the key is changed, + * calls the NotifyCallback callback + * @param key A key name to register (char *) + * @param callback A function pointer, takes a void * and a char * (NotifyCallback) + */ +_bx_export_ void sbuxton_register_notify(char *key, NotifyCallback callback); +/** + * Unregisters notifications for the key name (char *) key + * @param key A key name to unregister (char *) + */ +_bx_export_ void sbuxton_unregister_notify(char *key); /** * Creates a group if it does not exist and uses that group for all following get and set calls * If the group already exists, it will be used for all following get and set calls diff --git a/src/libbuxtonsimple/lbuxtonsimple.c b/src/libbuxtonsimple/lbuxtonsimple.c index 19a5eca..a9439ca 100644 --- a/src/libbuxtonsimple/lbuxtonsimple.c +++ b/src/libbuxtonsimple/lbuxtonsimple.c @@ -19,6 +19,7 @@ #include #include +#include "buxtonclient.h" #include "buxtonsimple.h" #include "buxtonsimple-internals.h" #include "log.h" @@ -29,14 +30,132 @@ extern BuxtonClient client; static char _layer[MAX_LG_LEN]; static char _group[MAX_LG_LEN]; static int saved_errno; +static int num_notify = 0; -/* Initialization of group */ -void sbuxton_set_group(char *group, char *layer) +/* return the client's file descriptor */ +int sbuxton_get_fd(void) +{ + if (!client) { + buxton_debug("No notifications registered, not connected\n"); + return -1; + } + + _BuxtonClient *c = NULL; + c = (_BuxtonClient *)client; + + return c->fd; +} + +/* wrapper for buxton_client_handle_response */ +ssize_t sbuxton_handle_response(void) +{ + if (!client) { + errno = ENOTCONN; + buxton_debug("No notifications registered, not connected\n"); + return -1; + } + + return buxton_client_handle_response(client); +} + +/* Register a key for notification */ +void sbuxton_register_notify(char *key, NotifyCallback callback) +{ + if (num_notify < 0) { + buxton_debug("Error in notification count\n"); + return; + } + + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return; + } + } + + saved_errno = errno; + BuxtonKey _key; + + _key = _buxton_notify_create(_layer, _group, key); + + if (!_key) { + errno = EACCES; + goto end; + return; + } + + nstatus *cb = malloc(sizeof(nstatus)); + + if (!cb) { + errno = ENOMEM; + goto end; + } + + cb->callback = callback; + cb->status = 0; + + if(buxton_register_notification(client, _key, _rn_cb, cb, true)) { + buxton_debug("Register notification call failed\n"); + errno = EACCES; + } else { + errno = saved_errno; + buxton_debug("Registration successful\n"); + ++num_notify; + } + +end: + if (num_notify == 0) { + _client_disconnect(); + } +} + +void sbuxton_unregister_notify(char *key) { - if (!_client_connection()) { + if (num_notify == 0) { + buxton_debug("No notifications registered\n"); + errno = EACCES; + return; + } + + if (!client) { + buxton_debug("No client connected\n"); errno = ENOTCONN; return; } + + saved_errno = errno; + BuxtonKey _key; + + _key = _buxton_notify_create(_layer, _group, key); + + if (!_key) { + errno = EACCES; + return; + } + + if (buxton_unregister_notification(client, _key, NULL, NULL, true)) { + buxton_debug("Unregister notification failed\n"); + errno = EACCES; + } else { + errno = saved_errno; + buxton_debug("Unregistration successful\n"); + --num_notify; + } + + if (num_notify == 0) { + _client_disconnect(); + } +} + +/* Initialization of group */ +void sbuxton_set_group(char *group, char *layer) +{ + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return; + } + } saved_errno = errno; int status = 0; /* strcpy the name of the layer and group*/ @@ -56,16 +175,20 @@ void sbuxton_set_group(char *group, char *layer) buxton_key_get_layer(g)); errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } } /* Set and get int32_t value for buxton key with type INT32 */ void sbuxton_set_int32(char *key, int32_t value) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, INT32); @@ -84,15 +207,19 @@ void sbuxton_set_int32(char *key, int32_t value) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } } int32_t sbuxton_get_int32(char *key) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return -1; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return 0; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, INT32); @@ -109,7 +236,9 @@ int32_t sbuxton_get_int32(char *key) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } return ret.val.i32val; } @@ -117,9 +246,11 @@ int32_t sbuxton_get_int32(char *key) void sbuxton_set_string(char *key, char *value ) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, STRING); @@ -137,15 +268,19 @@ void sbuxton_set_string(char *key, char *value ) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } } char* sbuxton_get_string(char *key) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return NULL; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return ""; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, STRING); @@ -162,7 +297,9 @@ char* sbuxton_get_string(char *key) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } return ret.val.sval; } @@ -170,9 +307,11 @@ char* sbuxton_get_string(char *key) void sbuxton_set_uint32(char *key, uint32_t value) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, UINT32); @@ -189,15 +328,19 @@ void sbuxton_set_uint32(char *key, uint32_t value) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } } uint32_t sbuxton_get_uint32(char *key) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return 0; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return 0; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, UINT32); @@ -214,7 +357,9 @@ uint32_t sbuxton_get_uint32(char *key) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } return ret.val.ui32val; } @@ -222,9 +367,11 @@ uint32_t sbuxton_get_uint32(char *key) void sbuxton_set_int64(char *key, int64_t value) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, INT64); @@ -241,15 +388,19 @@ void sbuxton_set_int64(char *key, int64_t value) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } } int64_t sbuxton_get_int64(char *key) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return -1; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return 0; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, INT64); @@ -266,7 +417,9 @@ int64_t sbuxton_get_int64(char *key) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } return ret.val.i64val; } @@ -274,9 +427,11 @@ int64_t sbuxton_get_int64(char *key) void sbuxton_set_uint64(char *key, uint64_t value) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, UINT64); @@ -293,15 +448,19 @@ void sbuxton_set_uint64(char *key, uint64_t value) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } } uint64_t sbuxton_get_uint64(char *key) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return 0; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return 0; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, UINT64); @@ -318,7 +477,9 @@ uint64_t sbuxton_get_uint64(char *key) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } return ret.val.ui64val; } @@ -326,9 +487,11 @@ uint64_t sbuxton_get_uint64(char *key) void sbuxton_set_float(char *key, float value) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, FLOAT); @@ -345,15 +508,19 @@ void sbuxton_set_float(char *key, float value) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } } float sbuxton_get_float(char *key) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return -1; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return 0; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, FLOAT); @@ -370,7 +537,9 @@ float sbuxton_get_float(char *key) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } return ret.val.fval; } @@ -378,9 +547,11 @@ float sbuxton_get_float(char *key) void sbuxton_set_double(char *key, double value) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, DOUBLE); @@ -397,15 +568,19 @@ void sbuxton_set_double(char *key, double value) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } } double sbuxton_get_double(char *key) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return -1; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return 0; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, DOUBLE); @@ -422,7 +597,9 @@ double sbuxton_get_double(char *key) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } return ret.val.dval; } @@ -430,9 +607,11 @@ double sbuxton_get_double(char *key) void sbuxton_set_bool(char *key, bool value) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, BOOLEAN); @@ -449,15 +628,19 @@ void sbuxton_set_bool(char *key, bool value) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } } bool sbuxton_get_bool(char *key) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return false; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return false; + } } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, BOOLEAN); @@ -474,7 +657,9 @@ bool sbuxton_get_bool(char *key) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } return ret.val.bval; } @@ -482,9 +667,11 @@ bool sbuxton_get_bool(char *key) void sbuxton_remove_group(char *group_name, char *layer) { /* make sure client connection is open */ - if (!_client_connection()) { - errno = ENOTCONN; - return; + if (!client) { + if (!_client_connection()) { + errno = ENOTCONN; + return; + } } saved_errno = errno; BuxtonKey group = _buxton_group_create(group_name, layer); @@ -497,7 +684,9 @@ void sbuxton_remove_group(char *group_name, char *layer) } else { errno = saved_errno; } - _client_disconnect(); + if (num_notify == 0) { + _client_disconnect(); + } } /* diff --git a/src/libbuxtonsimple/lbuxtonsimple.sym b/src/libbuxtonsimple/lbuxtonsimple.sym index fc54daa..1f2127d 100644 --- a/src/libbuxtonsimple/lbuxtonsimple.sym +++ b/src/libbuxtonsimple/lbuxtonsimple.sym @@ -1,5 +1,9 @@ BUXTONSIMPLE_1 { global: + sbuxton_get_fd; + sbuxton_handle_response; + sbuxton_register_notify; + sbuxton_unregister_notify; sbuxton_set_group; sbuxton_set_int32; sbuxton_get_int32; diff --git a/src/libbuxtonsimple/simple_test.c b/src/libbuxtonsimple/simple_test.c new file mode 100644 index 0000000..7329164 --- /dev/null +++ b/src/libbuxtonsimple/simple_test.c @@ -0,0 +1,222 @@ +/* + * This file is part of buxton. + * + * Copyright (C) 2014 Intel Corporation + * + * 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 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 "buxtonsimple.h" + +/* DEMONSTRATION */ +int main(void) +{ + //first test if stuff works with sbuxton_open and sbuxton_close + sbuxton_open(); + + /* Create group */ + errno = 0; + sbuxton_set_group("tg_s5", "user"); + printf("set_group: 'tg_s5', 'user', Error number: %s.\n", strerror(errno)); + + /* Test string setting */ + srand((unsigned)time(NULL)); + char * s1="Watermelon"; + printf("value should be set to %s.\n", s1); + errno = 0; + sbuxton_set_string("tk_s5", s1); + printf("set_string: 'tg_s5', 'tk_s5', Error number: %s.\n", strerror(errno)); + + /* Test string getting */ + char * sv1 = sbuxton_get_string("tk_s5"); + printf("Got value: %s(string).\n", sv1); + printf("get_string: 'tk_s5', Error number: %s.\n", strerror(errno)); + + //close connection + sbuxton_close(); + + //end testing with open and close + + printf("\n\nNow switch to opening and closing in each function\n\n"); + + /* Create group */ + errno = 0; + sbuxton_set_group("tg_s0", "user"); + printf("set_group: 'tg_s0', 'user', Error number: %s.\n", strerror(errno)); + + /* Test string setting */ + srand((unsigned)time(NULL)); + char * s="Watermelon"; + printf("value should be set to %s.\n", s); + errno = 0; + sbuxton_set_string("tk_s1", s); + printf("set_string: 'tg_s0', 'tk_s1', Error number: %s.\n", strerror(errno)); + + /* Test string getting */ + char * sv = sbuxton_get_string("tk_s1"); + printf("Got value: %s(string).\n", sv); + printf("get_string: 'tk_s1', Error number: %s.\n", strerror(errno)); + + /* Create group */ + errno = 0; + sbuxton_set_group("tg_s1", "user"); + printf("set_group: 'tg_s1', Error number: %s.\n", strerror(errno)); + + /* Test int32 setting */ + srand((unsigned)time(NULL)); + int32_t i=rand() % 100 + 1; + printf("value should be set to %i.\n",i); + errno = 0; + sbuxton_set_int32("tk_i32", i); + printf("set_int32: 'tg_s1', 'tk_i32', Error number: %s.\n", strerror(errno)); + + /* Create group */ + errno = 0; + sbuxton_set_group("tg_s2", "user"); + printf("set_group: 'tg_s2', Error number: %s.\n", strerror(errno)); + + /* Test int32 setting */ + srand((unsigned)time(NULL)); + int32_t i2=rand() % 1000 + 1; + printf("Second value should be set to %i.\n", i2); + errno = 0; + sbuxton_set_int32("tk_i32b", i2); + printf("set_int32: 'tg_s2', 'tk_i32b', Error number: %s.\n", strerror(errno)); + + /* Test int32 getting */ + /* Change group */ + errno = 0; + sbuxton_set_group("tg_s1", "user"); + printf("set_group: 'tg_s1', Error number: %s.\n", strerror(errno)); + errno = 0; + /* Get int32 */ + int32_t iv = sbuxton_get_int32("tk_i32"); + printf("get_int32: 'tg_s1', 'tk_i32', Error number: %s.\n", strerror(errno)); + printf("Got value: %i(int32_t).\n", iv); + errno = 0; + /* Change group */ + sbuxton_set_group("tg_s2", "user"); + printf("set_group: 'tg_s2', Error number: %s.\n", strerror(errno)); + errno = 0; + /* Get int32 */ + int32_t i2v = sbuxton_get_int32("tk_i32b"); + printf("Got value: %i(int32_t).\n", i2v); + printf("get_int32: 'tg_s2', 'tk_i32b', Error number: %s.\n", strerror(errno)); + + /* Create group */ + errno = 0; + sbuxton_set_group("tg_s3", "user"); + printf("set_group: 'tg_s3', Error number: %s.\n", strerror(errno)); + + /* Test uint32 setting */ + uint32_t ui32 = (uint32_t) rand() % 50 + 1; + printf("value should be set to %u.\n", ui32); + errno = 0; + sbuxton_set_uint32("tk_ui32", ui32); + printf("set_uint32: 'tg_s3', 'tk_ui32', Error number: %s.\n", strerror(errno)); + /* Test uint32 getting */ + errno = 0; + uint32_t ui32v = sbuxton_get_uint32("tk_ui32"); + printf("Got value: %i(uint32_t).\n", ui32v); + printf("get_uint32: 'tg_s3', 'tk_ui32', Error number: %s.\n", strerror(errno)); + + /* Test int64 setting */ + int64_t i64 = rand() % 1000 + 1; + printf("value should be set to ""%"PRId64".\n", i64); + errno = 0; + sbuxton_set_int64("tk_i64", i64); + /* Test int64 getting */ + errno = 0; + int64_t i64v = sbuxton_get_int64("tk_i64"); + printf("Got value: ""%"PRId64"(int64_t).\n", i64v); + printf("get_int64: 'tg_s3', 'tk_i64', Error number: %s.\n", strerror(errno)); + + /* Change group */ + errno = 0; + sbuxton_set_group("tg_s0", "user"); + + /* Test uint64 setting */ + uint64_t ui64 = (uint64_t) rand() % 500 + 1; + printf("value should be set to ""%"PRIu64".\n", ui64); + errno = 0; + sbuxton_set_uint64("tk_ui64", ui64); + /* Test uint64 getting */ + errno = 0; + uint64_t ui64v = sbuxton_get_uint64("tk_ui64"); + printf("Got value: ""%"PRIu64"(uint64_t).\n", ui64v); + printf("get_uint64: 'tg_s0', 'tk_ui64', Error number: %s.\n", strerror(errno)); + + /* Test float setting */ + float f = (float) (rand() % 9 + 1); + printf("value should be set to %e.\n", f); + errno = 0; + sbuxton_set_float("tk_f", f); + /* Test float getting */ + errno = 0; + float fv = sbuxton_get_float("tk_f"); + printf("Got value: %e(float).\n", fv); + printf("get_float: 'tg_s0', 'tk_f', Error number: %s.\n", strerror(errno)); + + /* Test double setting */ + double d = rand() % 7000 + 1; + printf("value should be set to %e.\n", d); + errno = 0; + sbuxton_set_double("tk_d", d); + /* Test double getting */ + errno = 0; + double dv = sbuxton_get_double("tk_d"); + printf("Got value: %e(double).\n", dv); + printf("get_double: 'tg_s0', 'tk_f', Error number: %s.\n", strerror(errno)); + + /* Test boolean setting */ + bool b = true; + printf("value should be set to %i.\n", b); + errno = 0; + sbuxton_set_bool("tk_b", b); + /* Test boolean getting */ + errno = 0; + bool bv = sbuxton_get_bool("tk_b"); + printf("Got value: %i(bool).\n", bv); + printf("get_bool: 'tg_s0', 'tk_b', Error number: %s.\n", strerror(errno)); + + /* Remove groups */ + errno = 0; + sbuxton_remove_group("tg_s1", "user"); + printf("remove_group: 'tg_s1', 'user', Error number: %s.\n", strerror(errno)); + errno = 0; + sbuxton_remove_group("tg_s0", "user"); + printf("remove_group: 'tg_s0', 'user', Error number: %s.\n", strerror(errno)); + errno = 0; + sbuxton_remove_group("tg_s2", "user"); + printf("remove_group: 'tg_s2', 'user', Error number: %s.\n", strerror(errno)); + errno = 0; + sbuxton_remove_group("tg_s3", "user"); + printf("remove_group: 'tg_s3', 'user', Error number: %s.\n", strerror(errno)); + + return 0; +} diff --git a/src/shared/buxtonsimple-internals.c b/src/shared/buxtonsimple-internals.c index 8c20b89..676baf2 100644 --- a/src/shared/buxtonsimple-internals.c +++ b/src/shared/buxtonsimple-internals.c @@ -16,10 +16,19 @@ #include "buxton.h" #include "buxtonsimple-internals.h" +#include "buxtonresponse.h" #include "log.h" BuxtonClient client = NULL; +typedef void (*NotifyCallback)(void *, char*); + +typedef struct nstatus { + int status; + NotifyCallback callback; +} nstatus; + +extern BuxtonClient client; /* Make sure client connection is open */ int _client_connection(void) { @@ -231,6 +240,136 @@ void _rg_cb(BuxtonResponse response, void *data) } } +/* Callback for buxton_register_notification */ +void _rn_cb(BuxtonResponse response, void *data) +{ + nstatus *ret = (nstatus *)data; + NotifyCallback cb; + BuxtonKey key = NULL; + char *name = NULL; + void *value = NULL; + + if (buxton_response_status(response) != 0) { + buxton_debug("Notify failed\n"); + ret->status = -1; + return; + } + + key = buxton_response_key(response); + name = buxton_key_get_name(key); + value = buxton_response_value(response); + + buxton_debug("Calling client cb....\n"); + cb = (NotifyCallback)ret->callback; + + cb(value, name); +} + +BuxtonKey _buxton_notify_create(char *layer, char *group, char *name) +{ + BuxtonKey key; + BuxtonDataType type = UNKNOWN; + char *stype; + + if (!group || !name) { + return NULL; + } + + if (!client) { + return NULL; + } + + key = buxton_key_create(group, name, layer, UNKNOWN); + + if (buxton_get_key_type(client, key, _gkt_cb, &type, true)) { + buxton_debug("Get key type call failed\n"); + return NULL; + } + + switch (type) { + case BUXTON_TYPE_MIN: + { + stype = "invalid- still min"; + } + case STRING: + { + stype = "string"; + break; + } + case INT32: + { + stype = "int32_t"; + break; + } + case UINT32: + { + stype = "uint32_t"; + break; + } + case INT64: + { + stype = "int64_t"; + break; + } + case UINT64: + { + stype = "uint64_t"; + break; + } + case FLOAT: + { + stype = "float"; + break; + } + case DOUBLE: + { + stype = "double"; + break; + } + case BOOLEAN: + { + stype = "bool"; + break; + } + default: + { + stype = "unknown"; + break; + } + } + + printf("type of key is: %d = %s\n", type, stype); + + if (type > BUXTON_TYPE_MIN && type < BUXTON_TYPE_MAX && type != UNKNOWN) { + BuxtonKey k = buxton_key_create(group, name, layer, type); + return k; + } else { + buxton_debug("Invalid type returned\n"); + } + + return NULL; +} + +void _gkt_cb(BuxtonResponse response, void *data) +{ + if (data == NULL) { + return; + } + + BuxtonDataType *ret = (BuxtonDataType*) data; + + if (buxton_response_status(response) != 0) { + + buxton_debug("Failed to get type\n"); + return; + } else { + buxton_debug("Get successful, got type\n"); + void *p = buxton_response_value(response); + *ret = *(BuxtonDataType*)p; + return; + } +} + /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * diff --git a/src/shared/buxtonsimple-internals.h b/src/shared/buxtonsimple-internals.h index 25b7b55..309b987 100644 --- a/src/shared/buxtonsimple-internals.h +++ b/src/shared/buxtonsimple-internals.h @@ -77,8 +77,21 @@ typedef struct vstatus { } val; } vstatus; +/* + * typedef for NotifyCallback, the format the client program's callback function + * to sbuxton_register_notify must be in + * +typedef void (*NotifyCallback)(void *, char*); + +typedef struct nstatus { + int status; + NotifyCallback callback; +} nstatus; */ + extern BuxtonClient client; +void _rn_cb(BuxtonResponse response, void *data); + /** * Checks for client connection and opens it if client connection is not open * @return Returns 1 on success and 0 on failure @@ -133,6 +146,23 @@ BuxtonKey _buxton_group_create(char *name, char *layer); */ void _rg_cb(BuxtonResponse response, void *data); +/** + * Creates a BuxtonKey internally for registering notifications + * with the correct buxton key type for that key + * @param layer A layer name that is a string (char *) + * @param group A group name that is a string (char *) + * @param name A group name that is a string (char *) + * @return A BuxtonKey that is a group + */ +BuxtonKey _buxton_notify_create(char *layer, char *group, char *name); + +/** + * Callback for buxton_get_key_type called in _buxton_notify_create + * @param response A BuxtonResponse + * @param data A voi pointer + */ +void _gkt_cb(BuxtonResponse response, void *data); + /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * diff --git a/test/check_buxtonsimple.c b/test/check_buxtonsimple.c index 91cdac0..4dc06ae 100644 --- a/test/check_buxtonsimple.c +++ b/test/check_buxtonsimple.c @@ -484,6 +484,105 @@ START_TEST (rg_cb_check) } END_TEST +void client_string_cb(void *data, char *key) +{ + fail_if(!data, "Data value is void\n"); + + char *val = (char *)data; + fail_if(strcmp(val, "test"), "Wrong data passed into client_string_cb"); + fail_if(strcmp(key, "keyname"), "Wrong key name passed into client_string_cb"); +} + +START_TEST (rn_cb_check) +{ + nstatus ns; + ns.status = 0; + ns.callback = client_string_cb; + BuxtonKey key = buxton_key_create("tg_s0", "keyname", "user", STRING); + + BuxtonData bd; + bd.type = STRING; + bd.store.d_string = buxton_string_pack("test"); + BuxtonArray *a = buxton_array_new(); + fail_if(!buxton_array_add(a, &bd), "Unable to add element to array"); + + _BuxtonResponse resp; + resp.data = a; + fail_if (!buxton_array_get(resp.data, 0), "No array in resp.data"); + resp.type = BUXTON_CONTROL_CHANGED; + BuxtonControlMessage bcm = buxton_response_type(&resp); + fail_if(bcm !=BUXTON_CONTROL_CHANGED, "Response type incorrect"); + resp.key = key; + + _rn_cb(&resp, &ns); +} +END_TEST + +START_TEST (buxton_notify_create_check) +{ + errno = 0; + sbuxton_set_group("tg_s0", "user"); + fail_if(errno == ENOTCONN, "Connection failed"); + int64_t int64_val = 5; + errno = 0; + sbuxton_set_int64("int64key", int64_val); + fail_if(errno == ENOTCONN, "Connection failed"); + fail_if(errno == EACCES, "Set int64 failed"); + errno = 0; + + int ret; + ret = _client_connection(); + fail_if(!ret, "Client connection failed- returned 0"); + fail_if(client == NULL, "could not open client connection"); + + BuxtonKey key = _buxton_notify_create("user", "tg_s0", "int64key"); + fail_if(!key, "Failed to create key"); + fail_if(strcmp(buxton_key_get_layer(key), "user"), + "Wrong key layer"); + fail_if(strcmp(buxton_key_get_name(key), "int64key"), + "Wrong key name"); + fail_if(strcmp(buxton_key_get_group(key), "tg_s0"), + "Wrong key group"); + fail_if(buxton_key_get_type(key) != INT64, "Wrong key type"); + + _client_disconnect(); + fail_if(client != NULL, "could not close client connection"); +} +END_TEST + +START_TEST (gkt_cb_check) +{ + BuxtonDataType data = UNKNOWN; + BuxtonDataType type = STRING; + + BuxtonKey key = buxton_key_create("tg_s0", "keyname", "user", UNKNOWN); + + BuxtonData bd1; + bd1.type = INT32; + bd1.store.d_int32 = 0; + BuxtonArray *a = buxton_array_new(); + fail_if(!buxton_array_add(a, &bd1), "Unable to add element to array"); + + BuxtonData bd; + bd.type = UINT32; + bd.store.d_uint32 = (uint32_t)type; + fail_if(!buxton_array_add(a, &bd), "Unable to add element to array"); + + _BuxtonResponse resp; + resp.data = a; + fail_if(!buxton_array_get(resp.data, 0), "No array in resp.data[0]"); + fail_if(!buxton_array_get(resp.data, 1), "No data in resp.data[1]"); + resp.type = BUXTON_CONTROL_GET_KEY_TYPE; + BuxtonControlMessage bcm = buxton_response_type(&resp); + fail_if(bcm !=BUXTON_CONTROL_GET_KEY_TYPE, "Response type incorrect"); + resp.key = key; + + _gkt_cb(&resp, &data); + + fail_if(data != STRING, "Did not get correct type"); +} +END_TEST + static Suite * buxtonsimp_suite(void) { @@ -524,6 +623,9 @@ buxtonsimp_suite(void) tcase_add_test(tc, bg_cb_check); tcase_add_test(tc, buxton_group_create_check); tcase_add_test(tc, rg_cb_check); + tcase_add_test(tc, rn_cb_check); + tcase_add_test(tc, buxton_notify_create_check); + tcase_add_test(tc, gkt_cb_check); suite_add_tcase(s, tc); return s;