diff options
author | Stoyan Bogdanov <stoyan.bogdanov@konsulko.com> | 2019-06-12 16:12:39 +0300 |
---|---|---|
committer | Stoyan Bogdanov <stoyan.bogdanov@konsulko.com> | 2019-06-18 11:41:45 +0300 |
commit | 089807fbbc4429ede68ef4fa846367345259de36 (patch) | |
tree | ada004340671ac5ee9b9b609ac646c637e8f3e98 | |
parent | 7eba64b591678e8e150ca5da0ea3537c3dd4db1f (diff) |
tests: network: Add test bindings for agl-service-network
- Remove agl-service-network-ctl.c from tests folder.
- Create the file and folders structure required.
for building test bindings. Add all required Cmake files.
- Create aft-agl-network.json configuration file for
testing binding.
- Create network.lua for testing bindings.
Bug-AGL: SPEC-2515
Signed-off-by: Stoyan Bogdanov <stoyan.bogdanov@konsulko.com>
Change-Id: I9df72a2d9d03189d3917c6e84f633af7603436b0
-rw-r--r-- | test/CMakeLists.txt | 38 | ||||
-rw-r--r-- | test/TEST-README.md | 30 | ||||
-rw-r--r-- | test/afb-test/CMakeLists.txt | 21 | ||||
-rw-r--r-- | test/afb-test/etc/CMakeLists.txt | 32 | ||||
-rw-r--r-- | test/afb-test/etc/aft-agl-network.json | 22 | ||||
-rw-r--r-- | test/afb-test/tests/CMakeLists.txt | 31 | ||||
-rw-r--r-- | test/afb-test/tests/network.lua | 52 | ||||
-rw-r--r-- | test/agl-service-network-ctl.c | 872 |
8 files changed, 213 insertions, 885 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 79065a4..2499cba 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,16 +1,28 @@ -########################################### -# build and install afb-client-demo -########################################### -PKG_CHECK_MODULES(libsystemd libsystemd>=222) -PKG_CHECK_MODULES(libafbwsc libafbwsc>=5.99) +########################################################################### +# Copyright 2019 Konsulko Group +# +# Author: Stoyan Bogdanov <stoyan.bogdanov@konsulko.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### -ADD_EXECUTABLE(agl-service-network-ctl agl-service-network-ctl.c) -TARGET_LINK_LIBRARIES(agl-service-network-ctl - ${link_libraries} - ${libsystemd_LDFLAGS} - ${libafbwsc_LDFLAGS} -) -INSTALL(TARGETS agl-service-network-ctl - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +# Include any directory not starting with _ +# ----------------------------------------------------- +PROJECT_SUBDIRS_ADD(${PROJECT_SRC_DIR_PATTERN}) + +ADD_TEST(NAME AGL_SERVICE_GPS_TESTS + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND afb-test.sh "${CMAKE_BINARY_DIR}/package" "${CMAKE_BINARY_DIR}/package-test" SERVICE + ) diff --git a/test/TEST-README.md b/test/TEST-README.md new file mode 100644 index 0000000..b2475ae --- /dev/null +++ b/test/TEST-README.md @@ -0,0 +1,30 @@ +## Building the test widgets +1. Source the SDK environment script +2. Create a build directory +3. Configure and build the project + +``` +mkdir build +cd build +cmake .. -DBUILD_TEST_WGT=TRUE +make +make widget + +``` +Note: If you omit the -DBUILD_TEST_WGT=TRUE parameter for cmake, +you'll have to type `make test_widget` to compile the test widget. + +This should produce two _.wgt_ files - one with the service +and one with the tests within the build directory. + +Run the tests: +``` +scp *.wgt root@<board-ip>:~ +ssh root@<board-ip> +afm-test $(ls *test.wgt) + +``` + +network.lua is testing just part of the verbs presented in the agl-service-network. +Proper testing of wifi, bluetooth and ethernet require known enviroment, +this is the reason why some of the vers are not tested. diff --git a/test/afb-test/CMakeLists.txt b/test/afb-test/CMakeLists.txt new file mode 100644 index 0000000..44bfeac --- /dev/null +++ b/test/afb-test/CMakeLists.txt @@ -0,0 +1,21 @@ +########################################################################### +# Copyright 2019 Konsulko Group +# +# Author: Stoyan Bogdanov <stoyan.bogdanov@konsulko.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# Include any directory not starting with _ +# ----------------------------------------------------- +PROJECT_SUBDIRS_ADD(${PROJECT_SRC_DIR_PATTERN}) diff --git a/test/afb-test/etc/CMakeLists.txt b/test/afb-test/etc/CMakeLists.txt new file mode 100644 index 0000000..eb0fdb8 --- /dev/null +++ b/test/afb-test/etc/CMakeLists.txt @@ -0,0 +1,32 @@ +########################################################################### +# Copyright 2019 Konsulko Group +# +# Author: Stoyan Bogdanov <stoyan.bogdanov@konsulko.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +################################################## +# network test configuration files +################################################## +PROJECT_TARGET_ADD(afb-test-config) + + file(GLOB CONF_FILES "*.json") + + add_input_files("${CONF_FILES}") + + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "TEST-CONFIG" + OUTPUT_NAME ${TARGET_NAME} + ) + diff --git a/test/afb-test/etc/aft-agl-network.json b/test/afb-test/etc/aft-agl-network.json new file mode 100644 index 0000000..495249a --- /dev/null +++ b/test/afb-test/etc/aft-agl-network.json @@ -0,0 +1,22 @@ +{ + "id": "http://iot.bzh/download/public/schema/json/ctl-schema.json#", + "": "http://iot.bzh/download/public/schema/json/ctl-schema.json#", + "metadata": { + "uid": "Test", + "version": "1.0", + "api": "aft-network", + "info": "AFB-test binding configuration file to test network api.", + "require": [ + "network-manager" + ] + }, + "testVerb": { + "uid": "launch_all_tests", + "info": "Launch all the tests", + "action": "lua://AFT#_launch_test", + "args": { + "trace": "network", + "files": ["network.lua"] + } + } +} diff --git a/test/afb-test/tests/CMakeLists.txt b/test/afb-test/tests/CMakeLists.txt new file mode 100644 index 0000000..b867d34 --- /dev/null +++ b/test/afb-test/tests/CMakeLists.txt @@ -0,0 +1,31 @@ +########################################################################### +# Copyright 2019 Konsulko Group +# +# Author: Stoyan Bogdanov <stoyan.bogdanov@konsulko.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +################################################## +# network Lua Scripts +################################################## +PROJECT_TARGET_ADD(test-files) + + file(GLOB LUA_FILES "*.lua" "*.sh") + add_input_files("${LUA_FILES}") + + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "TEST-DATA" + OUTPUT_NAME ${TARGET_NAME} + ) + diff --git a/test/afb-test/tests/network.lua b/test/afb-test/tests/network.lua new file mode 100644 index 0000000..2aebe23 --- /dev/null +++ b/test/afb-test/tests/network.lua @@ -0,0 +1,52 @@ +--[[ + Copyright 2019 Konsulko Group + + author:Stoyan Bogdanov <stoyan.bogdanov@konsulko.com> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + + +-- Test subscribe [working ok] +_AFT.testVerbStatusSuccess('testSubscribeASuccess','network-manager','subscribe', {value="global_state"}) +_AFT.testVerbStatusSuccess('testSubscribeBSuccess','network-manager','subscribe', {value="technologies"}) +_AFT.testVerbStatusSuccess('testSubscribeCSuccess','network-manager','subscribe', {value="technology_properties"}) +_AFT.testVerbStatusSuccess('testSubscribeDSuccess','network-manager','subscribe', {value="services"}) +_AFT.testVerbStatusSuccess('testSubscribeESuccess','network-manager','subscribe', {value="service_properties"}) +_AFT.testVerbStatusSuccess('testSubscribeFSuccess','network-manager','subscribe', {value="agent"}) + +-- Test unsubscribe +_AFT.testVerbStatusSuccess('testUnsubscribeASuccess','network-manager','unsubscribe', {value="global_state"}) +_AFT.testVerbStatusSuccess('testUnsubscribeBSuccess','network-manager','unsubscribe', {value="technologies"}) +_AFT.testVerbStatusSuccess('testUnsubscribeCSuccess','network-manager','unsubscribe', {value="technology_properties"}) +_AFT.testVerbStatusSuccess('testUnsubscribeDSuccess','network-manager','unsubscribe', {value="services"}) +_AFT.testVerbStatusSuccess('testUnsubscribeESuccess','network-manager','unsubscribe', {value="service_properties"}) +_AFT.testVerbStatusSuccess('testUnsubscribeFSuccess','network-manager','unsubscribe', {value="agent"}) + +_AFT.testVerbStatusSuccess('testStateSuccess','network-manager','state', {}) +_AFT.testVerbStatusSuccess('testOfflineSuccess','network-manager','offline', {}) +_AFT.testVerbStatusSuccess('testTechnologiesSuccess','network-manager','technologies', {}) +_AFT.testVerbStatusSuccess('testServicesSuccess','network-manager','services', {}) + +_AFT.testVerbStatusSuccess('testDisableTechnologySuccess','network-manager','disable_technology', {technology="ethernet"}) +_AFT.testVerbStatusSuccess('testEnableTechnologySuccess','network-manager', 'enable_technology', {technology="ethernet"}) + +_AFT.testVerbStatusSuccess('testDisableTechnologySuccess','network-manager','disable_technology', {technology="wifi"}) +_AFT.testVerbStatusSuccess('testEnableTechnologySuccess','network-manager', 'enable_technology', {technology="wifi"}) + +_AFT.testVerbStatusSuccess('testDisableTechnologySuccess','network-manager','disable_technology', {technology="bluetooth"}) +_AFT.testVerbStatusSuccess('testEnableTechnologySuccess','network-manager', 'enable_technology', {technology="bluetooth"}) + +_AFT.testVerbStatusSuccess('testgetpropertySuccess','network-manager','get_property', {technology="wifi"}) +_AFT.testVerbStatusSuccess('testgetpropertySuccess','network-manager','get_property', {technology="ethernet"}) +_AFT.testVerbStatusSuccess('testgetpropertySuccess','network-manager','get_property', {technology="bluetooth"}) diff --git a/test/agl-service-network-ctl.c b/test/agl-service-network-ctl.c deleted file mode 100644 index 6808b8d..0000000 --- a/test/agl-service-network-ctl.c +++ /dev/null @@ -1,872 +0,0 @@ -/* - * Copyright (C) 2018 Konsulko Group - * Author Pantelis Antoniou <pantelis.antoniou@konsulko.com> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE - -#include <stdlib.h> -#include <stdio.h> -#include <stdint.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <errno.h> -#include <getopt.h> -#include <stdbool.h> -#include <pthread.h> -#include <ctype.h> -#include <alloca.h> -#include <assert.h> - -#include <systemd/sd-event.h> -#include <json-c/json.h> - -#include <afb/afb-wsj1.h> -#include <afb/afb-ws-client.h> -#include <afb/afb-proto-ws.h> - -struct state { - const char *url; - int port; - const char *token; - const char *api; - char *uri; - bool interactive; - bool debug; - bool noexit; - - sd_event *loop; - struct afb_wsj1 *wsj1; - const char *proto; - bool hangup; -}; - -struct cmd { - const char *verb; - int (*call)(struct state *s, const struct cmd *c, int argc, char *argv[]); - void (*reply)(void *closure, struct afb_wsj1_msg *msg); -}; - -/* print usage of the program */ -/* declaration of functions */ -static void on_wsj1_hangup(void *closure, struct afb_wsj1 *wsj1); -static void on_wsj1_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg); -static void on_wsj1_event(void *closure, const char *event, struct afb_wsj1_msg *msg); - -/* the callback interface for wsj1 */ -static struct afb_wsj1_itf wsj1_itf = { - .on_hangup = on_wsj1_hangup, - .on_call = on_wsj1_call, - .on_event = on_wsj1_event -}; - -static void on_reply(struct state *s, const char *verb, struct afb_wsj1_msg *msg) -{ - printf("ON-REPLY %s: %s: %s\n%s\n", - s->api, - verb, - afb_wsj1_msg_is_reply_ok(msg) ? "OK" : "ERROR", - json_object_to_json_string_ext(afb_wsj1_msg_object_j(msg), - JSON_C_TO_STRING_PRETTY)); - fflush(stdout); - - /* if non interactive terminate */ - if (!s->interactive) { - if (!s->noexit) - sd_event_exit(s->loop, - afb_wsj1_msg_is_reply_ok(msg) ? 0 : EXIT_FAILURE); - else - printf("Ctrl-C to exit\n"); - } - -} - -static int do_call(struct state *s, const struct cmd *c, json_object *jparams) -{ - printf("CALL %s(%s)\n", c->verb, - jparams ? json_object_to_json_string(jparams) : ""); - - return afb_wsj1_call_j(s->wsj1, s->api, c->verb, jparams, c->reply, s); -} - -static int call_void(struct state *s, const struct cmd *c, int argc, char *argv[]) -{ - return do_call(s, c, NULL); -} - -static void on_ping_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "ping", msg); -} - -static void on_subscribe_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "subscribe", msg); -} - -static void on_unsubscribe_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "unsubscribe", msg); -} - -static int call_subscribe_unsubscribe(struct state *s, const struct cmd *c, int argc, char *argv[]) -{ - json_object *jobj; - - /* must give event name */ - if (argc < 1) - return -1; - jobj = json_object_new_object(); - json_object_object_add(jobj, "value", json_object_new_string(argv[0])); - - return do_call(s, c, jobj); -} - -static void on_state_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "state", msg); -} - -static void on_technologies_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "technologies", msg); -} - -static void on_services_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "services", msg); -} - -static void on_enable_technology_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "enable_technology", msg); -} - -static void on_disable_technology_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "disable_technology", msg); -} - -static int call_technology_arg(struct state *s, const struct cmd *c, int argc, char *argv[]) -{ - json_object *jobj; - - if (argc < 1) - return -1; - - jobj = json_object_new_object(); - json_object_object_add(jobj, "technology", json_object_new_string(argv[0])); - - return do_call(s, c, jobj); -} - -static void on_scan_services_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "scan_services", msg); -} - -static void on_offline_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "offline", msg); -} - -static int call_offline(struct state *s, const struct cmd *c, int argc, char *argv[]) -{ - json_object *jobj; - - /* with no arguments it's a getter */ - if (argc >= 1) { - jobj = json_object_new_object(); - json_object_object_add(jobj, "value", json_object_new_string(argv[0])); - } else - jobj = NULL; - - return do_call(s, c, jobj); -} - -static void on_connect_service_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "connect_service", msg); -} - -static int call_service_arg(struct state *s, const struct cmd *c, int argc, char *argv[]) -{ - json_object *jobj; - - if (argc < 1) - return -1; - - jobj = json_object_new_object(); - json_object_object_add(jobj, "service", json_object_new_string(argv[0])); - - return do_call(s, c, jobj); -} - -static void on_disconnect_service_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "disconnect_service", msg); -} - -static void on_remove_service_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "remove_service", msg); -} - -static void on_move_service_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "move_service", msg); -} - -static int call_move_service(struct state *s, const struct cmd *c, int argc, char *argv[]) -{ - json_object *jobj; - - /* 3 arguments and the middle must be before or after */ - if (argc < 3 || (strcmp(argv[1], "before") && strcmp(argv[1], "after"))) - return -1; - - jobj = json_object_new_object(); - json_object_object_add(jobj, "service", json_object_new_string(argv[0])); - json_object_object_add(jobj, - !strcmp(argv[1], "before") ? "before_service" : "after_service", - json_object_new_string(argv[2])); - - return do_call(s, c, jobj); -} - -static void on_set_property_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "set_property", msg); -} - -static void on_get_property_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "get_property", msg); -} - -static bool get_property_obj(json_object *jparent, int argc, char *argv[]) -{ - json_object *jobj, *jobj2; - int i, j, nest; - - for (i = 0; i < argc; i++) { - - if ((i < (argc - 1) && !strcmp(argv[i+1], "{"))) { - nest = 1; - for (j = i + 2; j < argc; j++) { - if (!strcmp(argv[j], "{")) - nest++; - else if (!strcmp(argv[j], "}")) - nest--; - - if (!nest) - break; - } - if (j >= argc) - return false; - - jobj = json_object_new_object(); - - jobj2 = json_object_new_array(); - get_property_obj(jobj2, j - i - 2, argv + i + 2); - json_object_object_add(jobj, argv[i], jobj2); - i = j; - } else - jobj = json_object_new_string(argv[i]); - - json_object_array_add(jparent, jobj); - - } - - return true; -} - -struct json_object *get_property_value(const char *str) -{ - char *le, *de; - long long ll; - double d; - - if (!strcmp(str, "null")) - return NULL; - if (!strcmp(str, "true")) - return json_object_new_boolean(true); - if (!strcmp(str, "false")) - return json_object_new_boolean(false); - - /* try both double and long and see which one is furthest */ - ll = strtoll(str, &le, 0); - d = strtod(str, &de); - - if (de > le) { - while (isspace(*de)) - de++; - if (!*de) - return json_object_new_double(d); - } else { - while (isspace(*le)) - le++; - if (!*le) - return json_object_new_int64((int64_t)ll); - } - - /* everything else is a string */ - return json_object_new_string(str); -} - -/* how many args is a single json object value */ -static int next_span(int pos, int argc, char *argv[]) -{ - const char *left, *right; - int j, nest; - - if (pos >= argc) - return 0; - - if (!strcmp(argv[pos], "{") || !strcmp(argv[pos], "[")) { - - if (!strcmp(argv[pos], "{")) { - left = "{"; - right = "}"; - } else { - left = "["; - right = "]"; - } - nest = 1; - for (j = pos + 1; nest > 0 && j < argc; j++) { - if (!strcmp(argv[j], left)) - nest++; - else if (!strcmp(argv[j], right)) - nest--; - } - if (nest && j >= argc) { - fprintf(stderr, "nesting error\n"); - return -1; - } - - return j - pos; - } - return 1; -} - -bool add_property_value(json_object *jparent, const char *key, int argc, char *argv[]) -{ - json_object *jobj; - const char *key2; - int i, span; - - if (!strcmp(argv[0], "[")) { - assert(!strcmp(argv[argc - 1], "]")); - - argc -= 2; - argv++; - - jobj = json_object_new_array(); - - for (i = 0; i < argc; ) { - span = next_span(i, argc, argv); - if (span < 0) { - fprintf(stderr, "bad nesting\n"); - return NULL; - } - - if (!add_property_value(jobj, NULL, span, argv + i)) { - fprintf(stderr, "error adding array\n"); - return false; - } - i += span; - - } - } else if (!strcmp(argv[0], "{")) { - assert(!strcmp(argv[argc - 1], "}")); - - argc -= 2; - argv++; - - jobj = json_object_new_object(); - - for (i = 0; i < argc; ) { - key2 = argv[i]; - - span = next_span(i + 1, argc, argv); - if (span < 0) { - fprintf(stderr, "bad nesting\n"); - return NULL; - } - - if (!add_property_value(jobj, key2, span, argv + i + 1)) { - fprintf(stderr, "error adding object\n"); - return false; - } - i += 1 + span; - } - } else { - jobj = get_property_value(argv[0]); - } - - if (key) - json_object_object_add(jparent, key, jobj); - else - json_object_array_add(jparent, jobj); - - return true; -} - -bool set_property_obj(json_object *jparent, int argc, char *argv[]) -{ - int i, span; - const char *key; - - for (i = 0; i < argc; ) { - - key = argv[i]; - if (!strcmp(key, "{") || !strcmp(key, "[")) { - fprintf(stderr, "Bad object start {\n"); - return false; - } - i++; - - if (i > (argc - 1)) { - fprintf(stderr, "out of arguments on key %s\n", key); - return false; - } - - span = next_span(i, argc, argv); - if (span < 0) { - fprintf(stderr, "bad nesting\n"); - return false; - } - - if (!add_property_value(jparent, key, span, argv + i)) { - fprintf(stderr, "error adding property\n"); - return false; - } - i += span; - } - - return true; -} - -static int call_get_set_property(struct state *s, const struct cmd *c, int argc, char *argv[]) -{ - json_object *jobj, *jprop = NULL; - const char *type; - int min_argc; - - if (argc < 1) - return -1; - - type = argv[0]; - /* global or technology or service */ - if (strcmp(type, "global") && strcmp(type, "technology") && - strcmp(type, "service")) { - fprintf(stderr, "unknown property type %s\n", type); - return -1; - } - - /* minimum is get global */ - min_argc = 1; - - /* technology or service have extra argument */ - if (!strcmp(type, "technology") || !strcmp(type, "service")) - min_argc++; - - /* set property must have an argument */ - min_argc += !strcmp(c->verb, "set_property") ? 2 : 0; - - if (argc < min_argc) { - fprintf(stderr, "not enough arguments\n"); - return -1; - } - - jobj = json_object_new_object(); - - argc--; - argv++; - - if (!strcmp(type, "technology")) { - json_object_object_add(jobj, "technology", - json_object_new_string(argv[0])); - argc--; - argv++; - } else if (!strcmp(type, "service")) { - json_object_object_add(jobj, "service", - json_object_new_string(argv[0])); - argc--; - argv++; - } - - /* get is array, set is object */ - if (!strcmp(c->verb, "get_property")) { - if (argc > 0) { - jprop = json_object_new_array(); - get_property_obj(jprop, argc, argv); - } - } else { - jprop = json_object_new_object(); - set_property_obj(jprop, argc, argv); - } - - if (jprop) - json_object_object_add(jobj, "properties", jprop); - - return do_call(s, c, jobj); -} - -static void on_agent_response_reply(void *closure, struct afb_wsj1_msg *msg) -{ - on_reply(closure, "agent_response", msg); -} - -static int call_agent_response(struct state *s, const struct cmd *c, int argc, char *argv[]) -{ - json_object *jresp = NULL, *jprop = NULL; - const char *method; - const char *propname; - int id; - - if (argc < 3) - return -1; - - method = *argv++; - argc--; - - id = (int)strtol(*argv++, NULL, 10); - argc--; - - if (id <= 0) { - fprintf(stderr, "bad agent response id %d\n", id); - return -1; - } - - propname = NULL; - if (!strcmp(method, "request-input")) { - propname = "fields"; - } else { - fprintf(stderr, "unknown agent response method '%s'\n", - method); - return -1; - } - - jresp = json_object_new_object(); - json_object_object_add(jresp, "method", - json_object_new_string(method)); - json_object_object_add(jresp, "id", - json_object_new_int(id)); - - if (argc > 0) { - jprop = json_object_new_object(); - set_property_obj(jprop, argc, argv); - json_object_object_add(jresp, propname, jprop); - } - - return do_call(s, c, jresp); -} - - -static const struct cmd cmds[] = { - { - .verb = "ping", - .call = call_void, - .reply = on_ping_reply, - }, { - .verb = "subscribe", - .call = call_subscribe_unsubscribe, - .reply = on_subscribe_reply, - }, { - .verb = "unsubscribe", - .call = call_subscribe_unsubscribe, - .reply = on_unsubscribe_reply, - }, { - .verb = "state", - .call = call_void, - .reply = on_state_reply, - }, { - .verb = "technologies", - .call = call_void, - .reply = on_technologies_reply, - }, { - .verb = "services", - .call = call_void, - .reply = on_services_reply, - }, { - .verb = "enable_technology", - .call = call_technology_arg, - .reply = on_enable_technology_reply, - }, { - .verb = "disable_technology", - .call = call_technology_arg, - .reply = on_disable_technology_reply, - }, { - .verb = "scan_services", - .call = call_technology_arg, - .reply = on_scan_services_reply, - }, { - .verb = "offline", - .call = call_offline, - .reply = on_offline_reply, - }, { - .verb = "connect_service", - .call = call_service_arg, - .reply = on_connect_service_reply, - }, { - .verb = "disconnect_service", - .call = call_service_arg, - .reply = on_disconnect_service_reply, - }, { - .verb = "remove_service", - .call = call_service_arg, - .reply = on_remove_service_reply, - }, { - .verb = "move_service", - .call = call_move_service, - .reply = on_move_service_reply, - }, { - .verb = "set_property", - .call = call_get_set_property, - .reply = on_set_property_reply, - }, { - .verb = "get_property", - .call = call_get_set_property, - .reply = on_get_property_reply, - }, { - .verb = "agent_response", - .call = call_agent_response, - .reply = on_agent_response_reply, - }, - { } -}; - -static int call_cmd(struct state *s, int argc, char *argv[]) -{ - const struct cmd *c; - const char *verb; - - /* first argument is verb */ - if (argc < 1 || !argv[0]) { - if (!s->interactive) - fprintf(stderr, "No verb given\n"); - goto out_err; - } - verb = argv[0]; - argv++; - argc--; - - for (c = cmds; c->verb; c++) { - if (!strcmp(verb, c->verb)) - return c->call(s, c, argc, argv); - } - - if (!s->interactive) - fprintf(stderr, "Unknown API verb \"%s\"\n", verb); -out_err: - - if (!s->interactive) - sd_event_exit(s->loop, EXIT_FAILURE); - - return -1; -} - -static struct option opts[] = { - { "url", required_argument, 0, 'u' }, - { "port", required_argument, 0, 'p' }, - { "token", required_argument, 0, 't' }, - { "api", required_argument, 0, 'a' }, - { "noexit", no_argument, 0, 'x' }, - { "debug", no_argument, 0, 'd' }, - { "help", no_argument, 0, 'h' }, - { } -}; - -#define DEFAULT_URL "ws://localhost" -#define DEFAULT_PORT 1234 -#define DEFAULT_TOKEN "HELLO" -#define DEFAULT_API "network-manager" -#define DEFAULT_DEBUG true - -static void usage(int status, const char *arg0) - __attribute__((__noreturn__)); - -static void usage(int status, const char *arg0) -{ - const char *name = strrchr(arg0, '/'); - FILE *outf = status ? stderr : stdout; - - name = name ? name + 1 : arg0; - - fprintf(outf, "usage: %s <options> [arguments]\n", name); - fprintf(outf, " options are:\n"); - fprintf(outf, " -u, --url=X URL to connect to (default %s)\n", DEFAULT_URL); - fprintf(outf, " -p, --port=X Port to use for connection (default %d)\n", DEFAULT_PORT); - fprintf(outf, " -t, --token=X Token to use (default %s)\n", DEFAULT_TOKEN); - fprintf(outf, " -a, --api=X API to use (default %s)\n", DEFAULT_API); - fprintf(outf, " -x, --noexit Do not exit in non-interactive mode (events)\n"); - fprintf(outf, " -d, --debug Enable debug printouts\n"); - fprintf(outf, " -h, -?, --help Display this help\n"); - - exit(status); -} - -/* entry function */ -int main(int argc, char **argv) -{ - int rc, ret, cc = 0, option_index; - const char *name; - struct state state, *s = &state; - - memset(s, 0, sizeof(*s)); - s->url = DEFAULT_URL; - s->port = DEFAULT_PORT; - s->token = DEFAULT_TOKEN; - s->api = DEFAULT_API; - s->debug = DEFAULT_DEBUG; - - s->interactive = false; - s->noexit = false; - - while ((cc = getopt_long(argc, argv, "u:p:t:a:xdh?", opts, - &option_index)) != -1) { - - if (cc == 0 && option_index >= 0) { - name = opts[option_index].name; - if (!name) - continue; - /* we don't have long options without short ones */ - usage(EXIT_FAILURE, argv[0]); - } - - switch (cc) { - case 'u': - s->url = optarg; - break; - case 'p': - s->port = atoi(optarg); - break; - case 't': - s->token = optarg; - break; - case 'a': - s->api = optarg; - break; - case 'x': - s->noexit = true; - break; - case 'd': - s->debug = true; - break; - case 'h': - case '?': - usage(0, argv[0]); - break; - } - } - - /* no arguments left, interactive mode */ - if (optind >= argc) { - printf("optind=%d\n", optind); - printf("argc=%d\n", argc); - s->interactive = true; - } - - ret = EXIT_FAILURE; - - rc = asprintf(&s->uri, "%s:%d/api?token=%s", s->url, s->port, s->token); - if (rc < 0) { - fprintf(stderr, "Unable to build URI\n"); - goto out; - } - - if (s->debug) { - fprintf(stderr, "URI: \"%s\"\n", s->uri); - fprintf(stderr, "API: \"%s\"\n", s->api); - fprintf(stderr, "interactive: %s\n", s->interactive ? "true" : "false"); - fprintf(stderr, "noexit: %s\n", s->noexit ? "true" : "false"); - } - - /* get the default event loop */ - rc = sd_event_default(&s->loop); - if (rc < 0) { - fprintf(stderr, "connection to default event loop failed: %s\n", strerror(-rc)); - goto out_no_event; - } - - /* connect the websocket wsj1 to the uri given by the first argument */ - s->wsj1 = afb_ws_client_connect_wsj1(s->loop, s->uri, &wsj1_itf, s); - if (s->wsj1 == NULL) { - fprintf(stderr, "connection to %s failed: %m\n", argv[1]); - goto out_no_wsj1; - } - - if (!s->interactive) { - ret = call_cmd(s, argc - optind, argv + optind); - if (ret < 0) { - fprintf(stderr, "command failed\n"); - sd_event_exit(s->loop, EXIT_FAILURE); - } - } else { - fprintf(stderr, "interactive mode not yet supported\n"); - sd_event_exit(s->loop, EXIT_FAILURE); - } - - ret = sd_event_loop(s->loop); - - /* cleanup */ - - afb_wsj1_unref(s->wsj1); -out_no_wsj1: - sd_event_unref(s->loop); -out_no_event: - free(s->uri); -out: - return ret; -} - -/* called when wsj1 hangsup */ -static void on_wsj1_hangup(void *closure, struct afb_wsj1 *wsj1) -{ - struct state *s = closure; - - printf("ON-HANGUP\n"); - fflush(stdout); - - s->hangup = true; - sd_event_exit(s->loop, 0); -} - -/* called when wsj1 receives a method invocation */ -static void on_wsj1_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg) -{ - int rc; - - printf("ON-CALL %s/%s:\n%s\n", api, verb, - json_object_to_json_string_ext(afb_wsj1_msg_object_j(msg), - JSON_C_TO_STRING_PRETTY)); - fflush(stdout); - rc = afb_wsj1_reply_error_s(msg, "\"unimplemented\"", NULL); - if (rc < 0) - fprintf(stderr, "replying failed: %m\n"); -} - -/* called when wsj1 receives an event */ -static void on_wsj1_event(void *closure, const char *event, struct afb_wsj1_msg *msg) -{ - printf("ON-EVENT %s:\n%s\n", event, - json_object_to_json_string_ext(afb_wsj1_msg_object_j(msg), - JSON_C_TO_STRING_PRETTY)); - fflush(stdout); -} |