{
"openapi": "3.0.0",
"$schema": "http:iot.bzh/download/openapi/schema-3.0/default-schema.json",
"info": {
"description": "",
"title": "ucs2",
"version": "1.0",
"x-binding-c-generator": {
"api": "UNICENS",
"version": 2,
"prefix": "ucs2_",
"postfix": "",
"start": null ,
"onevent": null,
"preinit": null,
"init": "ucs2_initbinding",
"scope": "",
"private": false
}
},
"servers": [
{
"url": "ws://{host}:{port}/api/monitor",
"description": "Unicens2 API.",
"variables": {
"host": {
"default": "localhost"
},
"port": {
"default": "1234"
}
},
"x-afb-events": [
{
"$ref": "#/components/schemas/afb-event"
}
]
}
],
"components": {
"schemas": {
"afb-reply": {
"$ref": "#/components/schemas/afb-reply-v2"
},
"afb-event": {
"$ref": "#/components/schemas/afb-event-v2"
},
"afb-reply-v2": {
"title": "Generic response.",
"type": "object",
"required": [ "jtype", "request" ],
"properties": {
"jtype": {
"type": "string",
"const": "afb-reply"
},
"request": {
"type": "object",
"required": [ "status" ],
"properties": {
"status": { "type": "string" },
"info": { "type": "string" },
"token": { "type": "string" },
"uuid": { "type": "string" },
"reqid": { "type": "string" }
}
},
"response": { "type": "object" }
}
},
"afb-event-v2": {
"type": "object",
"required": [ "jtype", "event" ],
"properties": {
"jtype": {
"type": "string",
"const": "afb-event"
},
"event": { "type": "string" },
"data": { "type": "object" }
}
}
},
"x-permissions": {
"config": {
"permission": "urn:AGL:permission:UNICENS:public:initialise"
},
"monitor": {
"permission": "urn:AGL:permission:UNICENS:public:monitor"
}
},
"responses": {
"200": {
"description": "A complex object array response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/afb-reply"
}
}
}
}
}
},
"paths": {
"/listconfig": {
"description": "List Config Files",
"get": {
"x-permissions": {
"$ref": "#/components/x-permissions/config"
},
"parameters": [
{
"in": "query",
"name": "cfgpath",
"required": false,
"schema": { "type": "string" }
}
],
"responses": {
"200": {"$ref": "#/components/responses/200"}
}
}
},
"/initialise": {
"descriptio/*
* 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);
}