mirror of
https://github.com/Telecominfraproject/wlan-ucentral-client.git
synced 2026-01-27 02:23:32 +00:00
Includes contents of /etc/ucentral/discovery.state.json (if present) in the "discovery" field of the connect message. This allows the controller to identify how the device discovered its cloud endpoint. Fixes: WIFI-14966 Signed-off-by: Marek Kwaczynski <marek@shasta.cloud>
1420 lines
36 KiB
C
1420 lines
36 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause */
|
|
|
|
#define _GNU_SOURCE
|
|
#include <stdio.h>
|
|
|
|
#include "ucentral.h"
|
|
|
|
static struct blob_buf proto;
|
|
static struct blob_buf result;
|
|
static struct blob_buf action;
|
|
static char *password;
|
|
static bool state_compress = true;
|
|
static int telemetry_interval;
|
|
static struct blob_attr *telemetry_filter;
|
|
|
|
enum {
|
|
JSONRPC_VER,
|
|
JSONRPC_METHOD,
|
|
JSONRPC_ERROR,
|
|
JSONRPC_PARAMS,
|
|
JSONRPC_ID,
|
|
JSONRPC_RADIUS,
|
|
__JSONRPC_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy jsonrpc_policy[__JSONRPC_MAX] = {
|
|
[JSONRPC_VER] = { .name = "jsonrpc", .type = BLOBMSG_TYPE_STRING },
|
|
[JSONRPC_METHOD] = { .name = "method", .type = BLOBMSG_TYPE_STRING },
|
|
[JSONRPC_ERROR] = { .name = "error", .type = BLOBMSG_TYPE_TABLE },
|
|
[JSONRPC_PARAMS] = { .name = "params", .type = BLOBMSG_TYPE_TABLE },
|
|
[JSONRPC_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
|
|
[JSONRPC_RADIUS] = { .name = "radius", .type = BLOBMSG_TYPE_STRING },
|
|
};
|
|
|
|
enum {
|
|
PARAMS_SERIAL,
|
|
PARAMS_UUID,
|
|
PARAMS_COMMAND,
|
|
PARAMS_CONFIG,
|
|
PARAMS_PAYLOAD,
|
|
PARAMS_REJECTED,
|
|
PARAMS_COMPRESS,
|
|
PARAMS_COMPRESS_64,
|
|
PARAMS_COMPRESS_SZ,
|
|
__PARAMS_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy params_policy[__PARAMS_MAX] = {
|
|
[PARAMS_SERIAL] = { .name = "serial", .type = BLOBMSG_TYPE_STRING },
|
|
[PARAMS_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_INT32 },
|
|
[PARAMS_CONFIG] = { .name = "config", .type = BLOBMSG_TYPE_TABLE },
|
|
[PARAMS_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
|
|
[PARAMS_PAYLOAD] = { .name = "payload", .type = BLOBMSG_TYPE_TABLE },
|
|
[PARAMS_REJECTED] = { .name = "rejected", .type = BLOBMSG_TYPE_ARRAY },
|
|
[PARAMS_COMPRESS] = { .name = "compress", .type = BLOBMSG_TYPE_BOOL },
|
|
[PARAMS_COMPRESS_64] = {.name = "compress_64", .type = BLOBMSG_TYPE_STRING},
|
|
[PARAMS_COMPRESS_SZ] = {.name = "compress_sz", .type = BLOBMSG_TYPE_INT32},
|
|
};
|
|
|
|
#if 0
|
|
static void
|
|
send_blob_frag(struct blob_buf *blob)
|
|
{
|
|
#define FRAG_SZ (128 * 1024)
|
|
char *msg, fragment[FRAG_SZ + LWS_PRE], *ptr;
|
|
int len, first = 1;
|
|
int cnt = 0;
|
|
FILE *fp = fopen("/dump", "w+");
|
|
ptr = msg = blobmsg_format_json(blob->head, true);
|
|
fprintf(fp, "%s", msg);
|
|
fclose(fp);
|
|
len = strlen(msg) + 1;
|
|
ULOG_DBG("TX: %s\n", msg);
|
|
if (!websocket) {
|
|
ULOG_ERR("trying to send data while not connected\n");
|
|
return;
|
|
}
|
|
|
|
do {
|
|
int opt = LWS_WRITE_TEXT;
|
|
int frag_len = len >= FRAG_SZ ? FRAG_SZ : len;
|
|
|
|
if (first && len > FRAG_SZ)
|
|
opt |= LWS_WRITE_NO_FIN;
|
|
else if (!first && len > FRAG_SZ)
|
|
opt = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
|
|
else if (!first && len <= FRAG_SZ)
|
|
opt = LWS_WRITE_CONTINUATION;
|
|
|
|
memcpy(&fragment[LWS_PRE], ptr, frag_len);
|
|
|
|
if (lws_write(websocket, (unsigned char *)&fragment[LWS_PRE], frag_len, opt) < 0)
|
|
ULOG_ERR("failed to send message\n");
|
|
len -= FRAG_SZ;
|
|
ptr += FRAG_SZ;
|
|
first = 0;
|
|
} while(len > 0);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
send_blob(struct blob_buf *blob)
|
|
{
|
|
char *msg;
|
|
int len;
|
|
|
|
msg = blobmsg_format_json(blob->head, true);
|
|
len = strlen(msg) + 1;
|
|
|
|
msg = realloc(msg, LWS_PRE + len);
|
|
memmove(&msg[LWS_PRE], msg, len);
|
|
memset(msg, 0, LWS_PRE);
|
|
|
|
ULOG_DBG("TX: %s\n", &msg[LWS_PRE]);
|
|
if (!websocket)
|
|
ULOG_ERR("trying to send data while not connected\n");
|
|
else if (lws_write(websocket, (unsigned char *)&msg[LWS_PRE], len - 1, LWS_WRITE_TEXT) < 0)
|
|
ULOG_ERR("failed to send message\n");
|
|
|
|
free(msg);
|
|
}
|
|
|
|
static void
|
|
proto_send_blob()
|
|
{
|
|
send_blob(&proto);
|
|
}
|
|
|
|
static void*
|
|
proto_new_blob(char *method)
|
|
{
|
|
blob_buf_init(&proto, 0);
|
|
blobmsg_add_string(&proto, "jsonrpc", "2.0");
|
|
blobmsg_add_string(&proto, "method", method);
|
|
return blobmsg_open_table(&proto, "params");
|
|
}
|
|
|
|
static void
|
|
result_send_blob()
|
|
{
|
|
send_blob(&result);
|
|
}
|
|
|
|
static void*
|
|
result_new_blob(uint32_t id, time_t uuid)
|
|
{
|
|
void *m;
|
|
|
|
blob_buf_init(&result, 0);
|
|
blobmsg_add_string(&result, "jsonrpc", "2.0");
|
|
blobmsg_add_u32(&result, "id", id);
|
|
m = blobmsg_open_table(&result, "result");
|
|
blobmsg_add_string(&result, "serial", client.serial);
|
|
blobmsg_add_u64(&result, "uuid", uuid);
|
|
|
|
return m;
|
|
}
|
|
|
|
void
|
|
password_notify(char *pwd)
|
|
{
|
|
void *m;
|
|
|
|
if (password) {
|
|
memset(password, 0, strlen(password));
|
|
free(password);
|
|
password = NULL;
|
|
}
|
|
|
|
if (!websocket) {
|
|
password = strdup(pwd);
|
|
return;
|
|
}
|
|
|
|
m = proto_new_blob("deviceupdate");
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
blobmsg_add_string(&proto, "currentPassword", pwd);
|
|
blobmsg_close_table(&proto, m);
|
|
ULOG_DBG("xmit password\n");
|
|
proto_send_blob();
|
|
memset(pwd, 0, strlen(pwd));
|
|
}
|
|
|
|
void
|
|
connect_send(void)
|
|
{
|
|
void *m = proto_new_blob("connect");
|
|
struct stat statbuf = { };
|
|
char path[PATH_MAX] = { };
|
|
void *c;
|
|
|
|
snprintf(path, PATH_MAX, "%s/capabilities.json", UCENTRAL_CONFIG);
|
|
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
blobmsg_add_string(&proto, "firmware", client.firmware);
|
|
if (client.recovery)
|
|
blobmsg_add_u64(&proto, "uuid", 0);
|
|
else
|
|
blobmsg_add_u64(&proto, "uuid", uuid_active ? uuid_active : 1);
|
|
if (client.boot_cause) {
|
|
blobmsg_add_string(&proto, "reason", client.boot_cause);
|
|
client.boot_cause = NULL;
|
|
} else {
|
|
blobmsg_add_string(&proto, "reason", "socket");
|
|
}
|
|
if (password) {
|
|
blobmsg_add_string(&proto, "password", password);
|
|
memset(password, 0, strlen(password));
|
|
free(password);
|
|
password = NULL;
|
|
}
|
|
if (!stat("/etc/ucentral/ucentral.defaults", &statbuf)) {
|
|
c = blobmsg_open_table(&proto, "defaults");
|
|
if (!blobmsg_add_json_from_file(&proto, "/etc/ucentral/ucentral.defaults")) {
|
|
log_send("failed to load defaults", LOG_ERR);
|
|
return;
|
|
}
|
|
blobmsg_close_table(&proto, c);
|
|
}
|
|
if (!stat("/etc/ucentral/discovery.state.json", &statbuf)) {
|
|
c = blobmsg_open_table(&proto, "discovery");
|
|
if (!blobmsg_add_json_from_file(&proto, "/etc/ucentral/discovery.state.json")) {
|
|
log_send("failed to load discovery state", LOG_ERR);
|
|
return;
|
|
}
|
|
blobmsg_close_table(&proto, c);
|
|
}
|
|
if (!stat("/tmp/udhcpc-vsi.json", &statbuf)) {
|
|
c = blobmsg_open_table(&proto, "udhcpc-vsi");
|
|
if (!blobmsg_add_json_from_file(&proto, "/tmp/udhcpc-vsi.json")) {
|
|
log_send("failed to load udhcpc-vsi", LOG_WARNING);
|
|
}
|
|
blobmsg_close_table(&proto, c);
|
|
}
|
|
c = blobmsg_open_table(&proto, "capabilities");
|
|
if (!blobmsg_add_json_from_file(&proto, path)) {
|
|
log_send("failed to load capabilities", LOG_ERR);
|
|
return;
|
|
}
|
|
blobmsg_close_table(&proto, c);
|
|
if (!stat("/etc/ucentral/restrictions.json", &statbuf))
|
|
blobmsg_add_u8(&proto, "restricted", 1);
|
|
blobmsg_close_table(&proto, m);
|
|
ULOG_DBG("xmit connect\n");
|
|
proto_send_blob();
|
|
}
|
|
|
|
void
|
|
ping_send(void)
|
|
{
|
|
void *m = proto_new_blob("ping");
|
|
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
if (uuid_active != uuid_latest) {
|
|
blobmsg_add_u64(&proto, "active", uuid_active);
|
|
blobmsg_add_u64(&proto, "uuid", uuid_latest);
|
|
} else
|
|
blobmsg_add_u64(&proto, "uuid", uuid_latest);
|
|
blobmsg_close_table(&proto, m);
|
|
ULOG_DBG("xmit ping\n");
|
|
proto_send_blob();
|
|
}
|
|
|
|
/*static void
|
|
pending_send(void)
|
|
{
|
|
void *m = proto_new_blob("cfgpending");
|
|
|
|
if (uuid_latest && uuid_active == uuid_latest)
|
|
return;
|
|
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
blobmsg_add_u64(&proto, "active", uuid_active);
|
|
blobmsg_add_u64(&proto, "uuid", uuid_latest);
|
|
blobmsg_close_table(&proto, m);
|
|
ULOG_DBG("xmit pending\n");
|
|
proto_send_blob();
|
|
}*/
|
|
|
|
void
|
|
raw_send(struct blob_attr *a)
|
|
{
|
|
enum {
|
|
RAW_METHOD,
|
|
RAW_PARAMS,
|
|
__RAW_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy raw_policy[__RAW_MAX] = {
|
|
[RAW_METHOD] = { .name = "method", .type = BLOBMSG_TYPE_STRING },
|
|
[RAW_PARAMS] = { .name = "params", .type = BLOBMSG_TYPE_TABLE },
|
|
};
|
|
|
|
struct blob_attr *tb[__PARAMS_MAX] = {};
|
|
struct blob_attr *b;
|
|
void *m;
|
|
size_t rem;
|
|
|
|
blobmsg_parse(raw_policy, __RAW_MAX, tb, blob_data(a), blob_len(a));
|
|
if (!tb[RAW_METHOD] || !tb[RAW_PARAMS])
|
|
return;
|
|
m = proto_new_blob(blobmsg_get_string(tb[RAW_METHOD]));
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
blobmsg_add_u64(&proto, "uuid", uuid_active);
|
|
blobmsg_for_each_attr(b, tb[RAW_PARAMS], rem)
|
|
blobmsg_add_blob(&proto, b);
|
|
blobmsg_close_table(&proto, m);
|
|
proto_send_blob();
|
|
}
|
|
|
|
void
|
|
event_send(struct blob_attr *a, time_t time)
|
|
{
|
|
struct blob_attr *b;
|
|
void *m, *d, *e, *p;
|
|
size_t rem;
|
|
|
|
m = proto_new_blob("event");
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
d = blobmsg_open_table(&proto, "data");
|
|
e = blobmsg_open_array(&proto, "event");
|
|
blobmsg_add_u64(&proto, NULL, time);
|
|
p = blobmsg_open_table(&proto, NULL);
|
|
blobmsg_for_each_attr(b, a, rem)
|
|
blobmsg_add_blob(&proto, b);
|
|
blobmsg_close_table(&proto, p);
|
|
blobmsg_close_array(&proto, e);
|
|
blobmsg_close_table(&proto, d);
|
|
blobmsg_close_table(&proto, m);
|
|
proto_send_blob();
|
|
}
|
|
|
|
void
|
|
radius_send(struct blob_attr *a)
|
|
{
|
|
enum {
|
|
RADIUS_TYPE,
|
|
RADIUS_DATA,
|
|
__RADIUS_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy radius_policy[__RADIUS_MAX] = {
|
|
[RADIUS_TYPE] = { .name = "radius", .type = BLOBMSG_TYPE_STRING },
|
|
[RADIUS_DATA] = { .name = "data", .type = BLOBMSG_TYPE_STRING },
|
|
};
|
|
|
|
struct blob_attr *tb[__PARAMS_MAX] = {};
|
|
|
|
blobmsg_parse(radius_policy, __RADIUS_MAX, tb, blob_data(a), blob_len(a));
|
|
if (!tb[RADIUS_TYPE] || !tb[RADIUS_DATA])
|
|
return;
|
|
blob_buf_init(&proto, 0);
|
|
blobmsg_add_string(&proto, "radius", blobmsg_get_string(tb[RADIUS_TYPE]));
|
|
blobmsg_add_string(&proto, "data", blobmsg_get_string(tb[RADIUS_DATA]));
|
|
proto_send_blob();
|
|
}
|
|
|
|
void
|
|
result_send(uint32_t id, struct blob_attr *a, uint32_t _uuid)
|
|
{
|
|
time_t uuid = (time_t) _uuid;
|
|
struct blob_attr *b;
|
|
void *m, *s;
|
|
size_t rem;
|
|
|
|
m = result_new_blob(id, uuid ? uuid : uuid_active);
|
|
s = blobmsg_open_table(&result, "status");
|
|
blobmsg_for_each_attr(b, a, rem)
|
|
blobmsg_add_blob(&result, b);
|
|
blobmsg_close_table(&result, s);
|
|
blobmsg_close_table(&result, m);
|
|
result_send_blob();
|
|
}
|
|
|
|
static char *stats_request_uuid;
|
|
|
|
static char *
|
|
comp(char *src, int len, int *rlen)
|
|
{
|
|
uLongf sourceLen = len;
|
|
uLongf destLen = compressBound(len);
|
|
unsigned char *dest = malloc(destLen);
|
|
|
|
memset(dest, 0, destLen);
|
|
|
|
if (compress(dest, &destLen, (unsigned char *)src, sourceLen) != Z_OK) {
|
|
printf("compress failed\n");
|
|
free(dest);
|
|
return NULL;
|
|
}
|
|
*rlen = destLen;
|
|
return (char *)dest;
|
|
}
|
|
|
|
static char *
|
|
decomp(char *src, int len)
|
|
{
|
|
uLong ucompSize = len + 1;
|
|
uLong compSize = compressBound(ucompSize);
|
|
char *dest = malloc(ucompSize);
|
|
|
|
if (uncompress((Bytef *)dest, &ucompSize, (Bytef *)src, compSize) != Z_OK)
|
|
{
|
|
free(dest);
|
|
return NULL;
|
|
};
|
|
return (char *)dest;
|
|
}
|
|
|
|
static char *
|
|
b64(char *src, int len)
|
|
{
|
|
char *dst;
|
|
int ret;
|
|
|
|
if (!src)
|
|
return NULL;
|
|
dst = malloc(len * 4);
|
|
ret = b64_encode(src, len, dst, len * 4);
|
|
if (ret < 1) {
|
|
free(dst);
|
|
return NULL;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static char *
|
|
decode_b64(char *src, int len)
|
|
{
|
|
char *dst;
|
|
int ret;
|
|
|
|
if (!src)
|
|
return NULL;
|
|
dst = malloc(len);
|
|
ret = b64_decode(src, dst, len);
|
|
if (ret < 1)
|
|
{
|
|
free(dst);
|
|
return NULL;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static void
|
|
decode_and_inflate(struct blob_attr **encoded_param, struct blob_attr *ret[__JSONRPC_MAX]) {
|
|
int compress_sz = blobmsg_get_u32(encoded_param[PARAMS_COMPRESS_SZ]);
|
|
char *cp64 = blobmsg_get_string(encoded_param[PARAMS_COMPRESS_64]);
|
|
char *cp = decode_b64(cp64, compress_sz);
|
|
if (cp == NULL) {
|
|
ULOG_ERR("base64 decode failed for message\n");
|
|
ret = NULL;
|
|
return;
|
|
}
|
|
char *params = decomp(cp, compress_sz);
|
|
if (params == NULL) {
|
|
ULOG_ERR("failed to uncompress message\n");
|
|
ret = NULL;
|
|
return;
|
|
}
|
|
|
|
static struct blob_buf pbuf;
|
|
blob_buf_init(&pbuf, 0);
|
|
void *c;
|
|
c = blobmsg_open_table(&pbuf, "params");
|
|
blobmsg_add_json_from_string(&pbuf, params);
|
|
free(params);
|
|
blobmsg_close_table(&pbuf, c);
|
|
blobmsg_parse(jsonrpc_policy, __JSONRPC_MAX, ret, blob_data(pbuf.head), blob_len(pbuf.head));
|
|
}
|
|
|
|
static void
|
|
stats_add_request_uuid(struct blob_buf *b)
|
|
{
|
|
if (!stats_request_uuid)
|
|
return;
|
|
blobmsg_add_string(b, "request_uuid", stats_request_uuid);
|
|
free(stats_request_uuid);
|
|
stats_request_uuid = NULL;
|
|
}
|
|
|
|
static char *
|
|
stats_get_string(struct blob_attr *a)
|
|
{
|
|
struct blob_attr *b;
|
|
size_t rem;
|
|
|
|
blob_buf_init(&result, 0);
|
|
stats_add_request_uuid(&result);
|
|
blobmsg_for_each_attr(b, a, rem)
|
|
blobmsg_add_blob(&result, b);
|
|
|
|
return blobmsg_format_json(result.head, true);
|
|
}
|
|
|
|
void
|
|
stats_send(struct blob_attr *a)
|
|
{
|
|
struct blob_attr *b;
|
|
void *c;
|
|
size_t rem;
|
|
|
|
blob_buf_init(&proto, 0);
|
|
blobmsg_add_string(&proto, "jsonrpc", "2.0");
|
|
blobmsg_add_string(&proto, "method", "state");
|
|
c = blobmsg_open_table(&proto, "params");
|
|
if (state_compress && blobmsg_data_len(a) >= 2 * 1024) {
|
|
char *source = stats_get_string(a);
|
|
int comp_len = 0, orig_len = strlen(source);
|
|
char *compressed = comp(source, orig_len, &comp_len);
|
|
char *encoded = b64(compressed, comp_len);
|
|
|
|
free(compressed);
|
|
free(source);
|
|
if (encoded) {
|
|
blobmsg_add_string(&proto, "compress_64", encoded);
|
|
blobmsg_add_u32(&proto, "compress_sz", orig_len);
|
|
free(encoded);
|
|
} else {
|
|
ULOG_ERR("failed to compress stats");
|
|
return;
|
|
}
|
|
} else {
|
|
stats_add_request_uuid(&proto);
|
|
blobmsg_for_each_attr(b, a, rem)
|
|
blobmsg_add_blob(&proto, b);
|
|
}
|
|
blobmsg_close_table(&proto, c);
|
|
proto_send_blob();
|
|
}
|
|
|
|
void
|
|
log_send(char *message, int severity)
|
|
{
|
|
void *m = proto_new_blob("log");
|
|
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
blobmsg_add_string(&proto, "log", message);
|
|
blobmsg_add_u32(&proto, "severity", severity);
|
|
blobmsg_close_table(&proto, m);
|
|
ULOG_ERR("%s\n", message);
|
|
proto_send_blob();
|
|
}
|
|
|
|
static char *health_request_uuid;
|
|
|
|
void
|
|
health_send(uint32_t sanity, struct blob_attr *a)
|
|
{
|
|
void *m = proto_new_blob("healthcheck");
|
|
struct blob_attr *b;
|
|
void *c;
|
|
size_t rem;
|
|
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
blobmsg_add_u64(&proto, "uuid", uuid_active);
|
|
if (health_request_uuid) {
|
|
blobmsg_add_string(&proto, "request_uuid", health_request_uuid);
|
|
free(health_request_uuid);
|
|
health_request_uuid = NULL;
|
|
}
|
|
blobmsg_add_u32(&proto, "sanity", sanity);
|
|
c = blobmsg_open_table(&proto, "data");
|
|
blobmsg_for_each_attr(b, a, rem)
|
|
blobmsg_add_blob(&proto, b);
|
|
blobmsg_close_table(&proto, c);
|
|
blobmsg_close_table(&proto, m);
|
|
proto_send_blob();
|
|
}
|
|
|
|
void
|
|
rebootlog_send(char *type, struct blob_attr *a)
|
|
{
|
|
void *m = proto_new_blob("rebootLog");
|
|
struct blob_attr *b;
|
|
void *c;
|
|
size_t rem;
|
|
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
blobmsg_add_u64(&proto, "uuid", uuid_active);
|
|
blobmsg_add_string(&proto, "type", type);
|
|
blobmsg_add_u64(&proto, "date", time(NULL));
|
|
c = blobmsg_open_array(&proto, "info");
|
|
blobmsg_for_each_attr(b, a, rem)
|
|
blobmsg_add_blob(&proto, b);
|
|
blobmsg_close_array(&proto, c);
|
|
blobmsg_close_table(&proto, m);
|
|
proto_send_blob();
|
|
}
|
|
|
|
void
|
|
result_send_error(uint32_t error, char *text, uint32_t retcode, uint32_t id)
|
|
{
|
|
void *c, *s;
|
|
|
|
ULOG_ERR("%s/(%d)\n", text, retcode);
|
|
|
|
c = result_new_blob(id, uuid_active);
|
|
s = blobmsg_open_table(&result, "status");
|
|
blobmsg_add_u32(&result, "error", error);
|
|
blobmsg_add_string(&result, "text", text);
|
|
blobmsg_add_u32(&result, "resultCode", retcode);
|
|
blobmsg_close_table(&result, s);
|
|
blobmsg_close_table(&result, c);
|
|
result_send_blob();
|
|
}
|
|
|
|
void
|
|
venue_broadcast_send(struct blob_attr *payload)
|
|
{
|
|
struct blob_attr *b;
|
|
void *m, *d;
|
|
size_t rem;
|
|
|
|
m = proto_new_blob("venue_broadcast");
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
blobmsg_add_u64(&proto, "timestamp", time(NULL));
|
|
d = blobmsg_open_table(&proto, "data");
|
|
blobmsg_for_each_attr(b, payload, rem)
|
|
blobmsg_add_blob(&proto, b);
|
|
blobmsg_close_table(&proto, d);
|
|
blobmsg_close_table(&proto, m);
|
|
proto_send_blob();
|
|
}
|
|
|
|
void
|
|
configure_reply(uint32_t error, char *text, time_t uuid, uint32_t id)
|
|
{
|
|
struct blob_attr *b;
|
|
void *c, *s, *r;
|
|
size_t rem;
|
|
|
|
if (!id)
|
|
return;
|
|
|
|
c = result_new_blob(id, uuid);
|
|
s = blobmsg_open_table(&result, "status");
|
|
blobmsg_add_string(&result, "text", text);
|
|
if (blob_len(rejected.head)) {
|
|
struct blob_attr *tb[__PARAMS_MAX] = {};
|
|
|
|
blobmsg_parse(params_policy, __PARAMS_MAX, tb, blob_data(rejected.head),
|
|
blob_len(rejected.head));
|
|
if (tb[PARAMS_REJECTED]) {
|
|
r = blobmsg_open_array(&result, "rejected");
|
|
blobmsg_for_each_attr(b, tb[PARAMS_REJECTED], rem)
|
|
blobmsg_add_blob(&result, b);
|
|
blobmsg_close_array(&result, r);
|
|
}
|
|
if (!error)
|
|
error = 1;
|
|
}
|
|
blobmsg_add_u32(&result, "error", error);
|
|
blobmsg_close_table(&result, s);
|
|
blobmsg_close_table(&result, c);
|
|
result_send_blob();
|
|
}
|
|
|
|
static void
|
|
configure_handle(struct blob_attr **rpc)
|
|
{
|
|
struct blob_attr *tb[__PARAMS_MAX] = {};
|
|
char *path = NULL;
|
|
uint32_t id = 0;
|
|
char *cfg;
|
|
FILE *fp;
|
|
|
|
blobmsg_parse(params_policy, __PARAMS_MAX, tb, blobmsg_data(rpc[JSONRPC_PARAMS]),
|
|
blobmsg_data_len(rpc[JSONRPC_PARAMS]));
|
|
|
|
if (rpc[JSONRPC_ID])
|
|
id = blobmsg_get_u32(rpc[JSONRPC_ID]);
|
|
|
|
if (tb[PARAMS_COMPRESS_64] && tb[PARAMS_COMPRESS_SZ])
|
|
{
|
|
ULOG_INFO("configuration message is compressed, decode and uncompress\n");
|
|
struct blob_attr *tb2[__JSONRPC_MAX] = {};
|
|
decode_and_inflate(tb,tb2);
|
|
if (!tb2[JSONRPC_PARAMS])
|
|
{
|
|
ULOG_ERR("after decode and uncompress, configure message is missing parameters\n");
|
|
configure_reply(1, "after decode and uncompress, configure message is missing parameters", 0, id);
|
|
return;
|
|
}
|
|
|
|
blobmsg_parse(params_policy, __PARAMS_MAX, tb, blobmsg_data(tb2[JSONRPC_PARAMS]),
|
|
blobmsg_data_len(tb2[JSONRPC_PARAMS]));
|
|
}
|
|
|
|
if (!tb[PARAMS_UUID] || !tb[PARAMS_SERIAL] || !tb[PARAMS_CONFIG]) {
|
|
ULOG_ERR("configure message is missing parameters\n");
|
|
configure_reply(1, "invalid parameters", 0, id);
|
|
return;
|
|
}
|
|
|
|
if (tb[PARAMS_COMPRESS])
|
|
state_compress = blobmsg_get_bool(tb[PARAMS_COMPRESS]);
|
|
|
|
if (asprintf(&path, "/etc/ucentral/ucentral.cfg.%010lu", (unsigned long int)blobmsg_get_u32(tb[PARAMS_UUID])) < 0) {
|
|
configure_reply(1, "failed to store the configuration", 0, id);
|
|
return;
|
|
}
|
|
|
|
fp = fopen(path, "w+");
|
|
free(path);
|
|
if (!fp) {
|
|
configure_reply(1, "failed to store the configuration", 0, id);
|
|
return;
|
|
}
|
|
cfg = blobmsg_format_json(tb[PARAMS_CONFIG], true);
|
|
if (!cfg) {
|
|
fclose(fp);
|
|
configure_reply(1, "failed to store the configuration", 0, id);
|
|
return;
|
|
}
|
|
fprintf(fp, "%s", cfg);
|
|
free(cfg);
|
|
fclose(fp);
|
|
config_init(1, id);
|
|
}
|
|
|
|
static void
|
|
action_handle(struct blob_attr **rpc, char *command, int reply, int delay, int admin, int timeout)
|
|
{
|
|
struct blob_attr *tb[__PARAMS_MAX] = {};
|
|
uint32_t id = 0;
|
|
|
|
blobmsg_parse(params_policy, __PARAMS_MAX, tb, blobmsg_data(rpc[JSONRPC_PARAMS]),
|
|
blobmsg_data_len(rpc[JSONRPC_PARAMS]));
|
|
|
|
if (rpc[JSONRPC_ID])
|
|
id = blobmsg_get_u32(rpc[JSONRPC_ID]);
|
|
|
|
if (!tb[PARAMS_SERIAL]) {
|
|
result_send_error(1, "invalid parameters", 1, id);
|
|
return;
|
|
}
|
|
|
|
blob_buf_init(&action, 0);
|
|
blobmsg_add_string(&action, "command", command);
|
|
blobmsg_add_u32(&action, "delay", delay);
|
|
blobmsg_add_u32(&action, "timeout", timeout ? timeout : 60 * 10);
|
|
if (rpc[JSONRPC_PARAMS]) {
|
|
void *c = blobmsg_open_table(&action, "payload");
|
|
struct blob_attr *b;
|
|
size_t rem;
|
|
|
|
blobmsg_for_each_attr(b, rpc[JSONRPC_PARAMS], rem)
|
|
blobmsg_add_blob(&action, b);
|
|
blobmsg_close_table(&action, c);
|
|
}
|
|
|
|
if (cmd_run(action.head, id, admin)) {
|
|
result_send_error(1, "failed to queue command", 1, id);
|
|
return;
|
|
}
|
|
if (reply)
|
|
result_send_error(0, "triggered command action", 0, id);
|
|
}
|
|
|
|
static void
|
|
error_handle(struct blob_attr **rpc)
|
|
{
|
|
enum {
|
|
ERROR_CODE,
|
|
ERROR_MESSAGE,
|
|
__ERROR_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy error_policy[__ERROR_MAX] = {
|
|
[ERROR_CODE] = { .name = "code", .type = BLOBMSG_TYPE_INT32 },
|
|
[ERROR_MESSAGE] = { .name = "message", .type = BLOBMSG_TYPE_STRING },
|
|
};
|
|
|
|
struct blob_attr *tb[__ERROR_MAX] = {};
|
|
uint32_t id = 0;
|
|
|
|
blobmsg_parse(error_policy, __ERROR_MAX, tb, blobmsg_data(rpc[JSONRPC_ERROR]),
|
|
blobmsg_data_len(rpc[JSONRPC_ERROR]));
|
|
|
|
if (!tb[ERROR_CODE] || !tb[ERROR_MESSAGE]) {
|
|
printf("%p %p\n", tb[ERROR_CODE], tb[ERROR_MESSAGE]);
|
|
result_send_error(1, "invalid parameters", 1, id);
|
|
return;
|
|
}
|
|
|
|
ULOG_ERR("error %d - %s\n", blobmsg_get_u32(tb[ERROR_CODE]), blobmsg_get_string(tb[ERROR_MESSAGE]));
|
|
}
|
|
|
|
static void
|
|
request_handle(struct blob_attr **rpc)
|
|
{
|
|
enum {
|
|
REQUEST_MESSAGE,
|
|
REQUEST_UUID,
|
|
__REQUEST_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy request_policy[__REQUEST_MAX] = {
|
|
[REQUEST_MESSAGE] = { .name = "message", .type = BLOBMSG_TYPE_STRING },
|
|
[REQUEST_UUID] = { .name = "request_uuid", .type = BLOBMSG_TYPE_STRING },
|
|
};
|
|
|
|
struct blob_attr *tb[__REQUEST_MAX] = {};
|
|
char *message, *uuid;
|
|
uint32_t id = 0;
|
|
int ret;
|
|
|
|
blobmsg_parse(request_policy, __REQUEST_MAX, tb, blobmsg_data(rpc[JSONRPC_PARAMS]),
|
|
blobmsg_data_len(rpc[JSONRPC_PARAMS]));
|
|
|
|
if (rpc[JSONRPC_ID])
|
|
id = blobmsg_get_u32(rpc[JSONRPC_ID]);
|
|
|
|
if (!tb[REQUEST_MESSAGE] || !tb[REQUEST_UUID]) {
|
|
result_send_error(1, "invalid parameters", 1, id);
|
|
return;
|
|
}
|
|
|
|
message = blobmsg_get_string(tb[REQUEST_MESSAGE]);
|
|
uuid = blobmsg_get_string(tb[REQUEST_UUID]);
|
|
|
|
if (!strcmp(message, "state")) {
|
|
stats_request_uuid = strdup(uuid);
|
|
ret = system("/etc/init.d/ustats restart");
|
|
if (ret) {
|
|
result_send_error(1, "failed to execute ustats", ret, id);
|
|
return;
|
|
}
|
|
} else if (!strcmp(message, "healthcheck")) {
|
|
health_request_uuid = strdup(uuid);
|
|
ret = system("/etc/init.d/uhealth restart");
|
|
if (ret) {
|
|
result_send_error(1, "failed to execute uhealth", ret, id);
|
|
return;
|
|
}
|
|
} else {
|
|
result_send_error(1, "invalid parameters", 1, id);
|
|
return;
|
|
}
|
|
result_send_error(0, "success", 0, id);
|
|
}
|
|
|
|
static void
|
|
leds_handle(struct blob_attr **rpc)
|
|
{
|
|
enum {
|
|
LED_DURATION,
|
|
LED_PATTERN,
|
|
LED_ENDLESS,
|
|
__LED_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy led_policy[__LED_MAX] = {
|
|
[LED_DURATION] = { .name = "duration", .type = BLOBMSG_TYPE_INT32 },
|
|
[LED_PATTERN] = { .name = "pattern", .type = BLOBMSG_TYPE_STRING },
|
|
[LED_ENDLESS] = { .name = "endless", .type = BLOBMSG_TYPE_BOOL },
|
|
};
|
|
|
|
struct blob_attr *tb[__LED_MAX] = {};
|
|
uint32_t duration = 0;
|
|
uint32_t id = 0;
|
|
|
|
blobmsg_parse(led_policy, __LED_MAX, tb, blobmsg_data(rpc[JSONRPC_PARAMS]),
|
|
blobmsg_data_len(rpc[JSONRPC_PARAMS]));
|
|
|
|
if (rpc[JSONRPC_ID])
|
|
id = blobmsg_get_u32(rpc[JSONRPC_ID]);
|
|
|
|
if (tb[LED_DURATION])
|
|
duration = blobmsg_get_u32(tb[LED_DURATION]);
|
|
|
|
if (tb[LED_ENDLESS] && blobmsg_get_bool(tb[LED_ENDLESS]))
|
|
duration = 0xffff;
|
|
|
|
if (!strcmp(blobmsg_get_string(tb[LED_PATTERN]), "blink")) {
|
|
result_send_error(0, "success", 0, id);
|
|
ubus_blink_leds(duration);
|
|
return;
|
|
}
|
|
ubus_blink_leds(0);
|
|
action_handle(rpc, "leds", 1, 1, 1, 0);
|
|
}
|
|
|
|
static void
|
|
event_handle(struct blob_attr **rpc)
|
|
{
|
|
enum {
|
|
REALTIME_TYPES,
|
|
__REALTIME_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy event_policy[__REALTIME_MAX] = {
|
|
[REALTIME_TYPES] = { .name = "types", .type = BLOBMSG_TYPE_ARRAY },
|
|
};
|
|
|
|
struct blob_attr *tb[__REALTIME_MAX] = {};
|
|
struct blob_attr *b;
|
|
uint32_t id = 0;
|
|
void *m, *s;
|
|
size_t rem;
|
|
|
|
blobmsg_parse(event_policy, __REALTIME_MAX, tb, blobmsg_data(rpc[JSONRPC_PARAMS]),
|
|
blobmsg_data_len(rpc[JSONRPC_PARAMS]));
|
|
|
|
if (rpc[JSONRPC_ID])
|
|
id = blobmsg_get_u32(rpc[JSONRPC_ID]);
|
|
|
|
m = result_new_blob(id, uuid_active);
|
|
s = blobmsg_open_table(&result, "status");
|
|
blobmsg_add_u32(&result, "error", 0);
|
|
blobmsg_add_string(&result, "text", "Success");
|
|
blobmsg_close_table(&result, s);
|
|
s = blobmsg_open_table(&result, "events");
|
|
if (tb[REALTIME_TYPES])
|
|
blobmsg_for_each_attr(b, tb[REALTIME_TYPES], rem) {
|
|
if (blobmsg_type(b) != BLOBMSG_TYPE_STRING)
|
|
continue;
|
|
event_dump(&result, blobmsg_get_string(b), true);
|
|
}
|
|
else
|
|
event_dump_all(&result);
|
|
|
|
blobmsg_close_table(&result, s);
|
|
blobmsg_close_table(&result, m);
|
|
result_send_blob();
|
|
}
|
|
|
|
void
|
|
telemetry_periodic(void)
|
|
{
|
|
void *m, *s;
|
|
int count = 0;
|
|
|
|
m = proto_new_blob("telemetry");
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
blobmsg_add_u8(&proto, "adhoc", 1);
|
|
s = blobmsg_open_table(&proto, "data");
|
|
count += event_dump(&proto, "dhcp-snooping", true);
|
|
count += event_dump(&proto, "wifi-frames", true);
|
|
count += event_dump(&proto, "event", true);
|
|
blobmsg_close_table(&proto, s);
|
|
blobmsg_close_table(&proto, m);
|
|
if (count)
|
|
proto_send_blob();
|
|
}
|
|
|
|
static void
|
|
telemetry_complete_cb(struct task *t, time_t uuid, uint32_t id, int ret)
|
|
{
|
|
struct blob_attr *b;
|
|
void *m, *s;
|
|
size_t rem;
|
|
|
|
m = proto_new_blob("telemetry");
|
|
blobmsg_add_string(&proto, "serial", client.serial);
|
|
s = blobmsg_open_table(&proto, "data");
|
|
blobmsg_for_each_attr(b, telemetry_filter, rem) {
|
|
if (blobmsg_type(b) != BLOBMSG_TYPE_STRING)
|
|
continue;
|
|
event_dump(&proto, blobmsg_get_string(b), true);
|
|
}
|
|
blobmsg_close_table(&proto, s);
|
|
blobmsg_close_table(&proto, m);
|
|
proto_send_blob();
|
|
}
|
|
|
|
static void
|
|
telemetry_run_cb(time_t uuid, uint32_t _id)
|
|
{
|
|
ULOG_INFO("running telemetry task\n");
|
|
|
|
execlp("/usr/share/ucentral/telemetry.uc", "/usr/share/ucentral/telemetry.uc", NULL);
|
|
exit(1);
|
|
}
|
|
|
|
struct task telemetry_task = {
|
|
.run_time = 60,
|
|
.run = telemetry_run_cb,
|
|
.complete = telemetry_complete_cb,
|
|
};
|
|
|
|
static void
|
|
telemetry_handle(struct blob_attr **rpc)
|
|
{
|
|
enum {
|
|
TELEMETRY_INTERVAL,
|
|
TELEMETRY_TYPES,
|
|
__TELEMETRY_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy telemetry_policy[__TELEMETRY_MAX] = {
|
|
[TELEMETRY_INTERVAL] = { .name = "interval", .type = BLOBMSG_TYPE_INT32 },
|
|
[TELEMETRY_TYPES] = { .name = "types", .type = BLOBMSG_TYPE_ARRAY },
|
|
};
|
|
|
|
struct blob_attr *tb[__TELEMETRY_MAX] = {};
|
|
uint32_t id = 0;
|
|
void *m, *s;
|
|
int err = 0;
|
|
|
|
blobmsg_parse(telemetry_policy, __TELEMETRY_MAX, tb, blobmsg_data(rpc[JSONRPC_PARAMS]),
|
|
blobmsg_data_len(rpc[JSONRPC_PARAMS]));
|
|
|
|
if (rpc[JSONRPC_ID])
|
|
id = blobmsg_get_u32(rpc[JSONRPC_ID]);
|
|
|
|
if (tb[TELEMETRY_INTERVAL])
|
|
telemetry_interval = blobmsg_get_u32(tb[TELEMETRY_INTERVAL]);
|
|
else
|
|
telemetry_interval = 0;
|
|
if (telemetry_filter) {
|
|
free(telemetry_filter);
|
|
telemetry_filter = NULL;
|
|
}
|
|
if (tb[TELEMETRY_TYPES])
|
|
telemetry_filter = blob_memdup(tb[TELEMETRY_TYPES]);
|
|
|
|
if (telemetry_interval && !telemetry_filter) {
|
|
err = 2;
|
|
telemetry_interval = 0;
|
|
}
|
|
if (client.telemetry_interval) {
|
|
err = 3;
|
|
} else if (!telemetry_interval) {
|
|
task_stop(&telemetry_task);
|
|
unlink("/tmp/ucentral.telemetry");
|
|
} else if (telemetry_task.periodic) {
|
|
err = 2;
|
|
} else {
|
|
event_flush();
|
|
telemetry_task.periodic = telemetry_interval;
|
|
task_telemetry(&telemetry_task, uuid_latest, id);
|
|
}
|
|
|
|
m = result_new_blob(id, uuid_active);
|
|
s = blobmsg_open_table(&result, "status");
|
|
blobmsg_add_u32(&result, "error", err);
|
|
switch (err) {
|
|
case 0:
|
|
blobmsg_add_string(&result, "text", "Success");
|
|
break;
|
|
case 3:
|
|
blobmsg_add_string(&result, "text", "Periodic telemetry is enabled");
|
|
break;
|
|
default:
|
|
blobmsg_add_string(&result, "text", "Invalid Arguments");
|
|
break;
|
|
}
|
|
blobmsg_close_table(&result, s);
|
|
blobmsg_close_table(&result, m);
|
|
result_send_blob();
|
|
}
|
|
|
|
static void
|
|
ping_handle(struct blob_attr **rpc)
|
|
{
|
|
uint32_t id = 0;
|
|
void *m, *s;
|
|
|
|
if (rpc[JSONRPC_ID])
|
|
id = blobmsg_get_u32(rpc[JSONRPC_ID]);
|
|
|
|
m = result_new_blob(id, uuid_active);
|
|
s = blobmsg_open_table(&result, "status");
|
|
blobmsg_add_u32(&result, "error", 0);
|
|
blobmsg_add_string(&result, "text", "Success");
|
|
blobmsg_close_table(&result, s);
|
|
blobmsg_close_table(&result, m);
|
|
result_send_blob();
|
|
}
|
|
|
|
static void
|
|
package_handle(struct blob_attr **rpc)
|
|
{
|
|
enum {
|
|
PACKAGE_NAME,
|
|
PACKAGE_URL,
|
|
PACKAGE_RESULT,
|
|
__PACKAGE_MAX,
|
|
};
|
|
|
|
enum {
|
|
ROOT_OP,
|
|
ROOT_PACKAGE,
|
|
ROOT_PACKAGES,
|
|
ROOT_SERIAL,
|
|
__ROOT_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy package_policy[__PACKAGE_MAX] = {
|
|
[PACKAGE_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
|
|
[PACKAGE_URL] = { .name = "url", .type = BLOBMSG_TYPE_STRING },
|
|
[PACKAGE_RESULT] = { .name = "result", .type = BLOBMSG_TYPE_STRING },
|
|
};
|
|
|
|
static const struct blobmsg_policy root_policy[__ROOT_MAX] = {
|
|
[ROOT_OP] = { .name = "op", .type = BLOBMSG_TYPE_STRING },
|
|
[ROOT_PACKAGE] = { .name = "package", .type = BLOBMSG_TYPE_STRING },
|
|
[ROOT_PACKAGES] = { .name = "packages", .type = BLOBMSG_TYPE_ARRAY },
|
|
[ROOT_SERIAL] = { .name = "serial", .type = BLOBMSG_TYPE_STRING },
|
|
};
|
|
|
|
struct blob_attr *tb_root[__ROOT_MAX] = {};
|
|
struct blob_attr *tb[__PACKAGE_MAX] = {};
|
|
struct blob_attr *cur;
|
|
uint32_t id = 0;
|
|
int rem;
|
|
int error_count = 0;
|
|
|
|
if (rpc[JSONRPC_ID])
|
|
id = blobmsg_get_u32(rpc[JSONRPC_ID]);
|
|
|
|
if (!rpc[JSONRPC_PARAMS]) {
|
|
result_send_error(1, "invalid parameters: params missing", 1, id);
|
|
return;
|
|
}
|
|
|
|
blobmsg_parse(root_policy, __ROOT_MAX, tb_root, blobmsg_data(rpc[JSONRPC_PARAMS]), blobmsg_data_len(rpc[JSONRPC_PARAMS]));
|
|
|
|
if (!tb_root[ROOT_OP]) {
|
|
result_send_error(1, "invalid parameters: missing operation", 1, id);
|
|
return;
|
|
}
|
|
|
|
const char *op = blobmsg_get_string(tb_root[ROOT_OP]);
|
|
if (strcmp(op, "install") && strcmp(op, "delete") && strcmp(op, "list")) {
|
|
result_send_error(1, "invalid parameters: unrecognized operation", 1, id);
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(op, "list")) {
|
|
if (!tb_root[ROOT_PACKAGE]) {
|
|
result_send_error(1, "invalid parameters: missing package name", 1, id);
|
|
return;
|
|
}
|
|
|
|
if (blobmsg_type(tb_root[ROOT_PACKAGE]) != BLOBMSG_TYPE_STRING) {
|
|
result_send_error(1, "invalid parameters: package must be a string", 1, id);
|
|
return;
|
|
}
|
|
const char *result_str = cpm_list(blobmsg_get_string(tb_root[ROOT_PACKAGE]));
|
|
|
|
if (strcmp(result_str, "Success")) {
|
|
result_send_error(1, "No such package.", 1, id);
|
|
}
|
|
|
|
void *m, *s;
|
|
m = result_new_blob(id, uuid_active);
|
|
s = blobmsg_open_table(&result, "status");
|
|
|
|
struct stat statbuf = { };
|
|
if (!stat("/tmp/package.version", &statbuf)) {
|
|
// Read the file content into a string
|
|
FILE *fp = fopen("/tmp/package.version", "r");
|
|
if (!fp) {
|
|
log_send("failed to open package.version", LOG_ERR);
|
|
blobmsg_close_table(&result, s);
|
|
blobmsg_close_table(&result, m);
|
|
return;
|
|
}
|
|
|
|
char *source = malloc(statbuf.st_size + 1);
|
|
if (!source) {
|
|
log_send("failed to allocate memory for package.version", LOG_ERR);
|
|
fclose(fp);
|
|
blobmsg_close_table(&result, s);
|
|
blobmsg_close_table(&result, m);
|
|
return;
|
|
}
|
|
|
|
size_t read_size = fread(source, 1, statbuf.st_size, fp);
|
|
fclose(fp);
|
|
source[read_size] = '\0';
|
|
|
|
// int comp_len = 0;
|
|
// char *compressed = comp(source, read_size, &comp_len);
|
|
// char *encoded = b64(compressed, comp_len);
|
|
|
|
blobmsg_add_string(&result, "package", source);
|
|
}
|
|
|
|
blobmsg_add_string(&result, "text", "Success");
|
|
blobmsg_close_table(&result, s);
|
|
blobmsg_close_table(&result, m);
|
|
result_send_blob();
|
|
}
|
|
else {
|
|
if (!tb_root[ROOT_PACKAGES]) {
|
|
result_send_error(1, "invalid parameters: missing packages array", 1, id);
|
|
return;
|
|
}
|
|
|
|
if (blobmsg_type(tb_root[ROOT_PACKAGES]) != BLOBMSG_TYPE_ARRAY) {
|
|
result_send_error(1, "invalid parameters: packages must be an array", 1, id);
|
|
return;
|
|
}
|
|
|
|
blobmsg_for_each_attr(cur, tb_root[ROOT_PACKAGES], rem) {
|
|
if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) {
|
|
result_send_error(1, "invalid parameters: package array elements must be objects", 1, id);
|
|
return;
|
|
}
|
|
|
|
blobmsg_parse(package_policy, __PACKAGE_MAX, tb, blobmsg_data(cur), blobmsg_data_len(cur));
|
|
|
|
if (!tb[PACKAGE_NAME]) {
|
|
result_send_error(1, "invalid parameters: missing package name", 1, id);
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(op, "install")) {
|
|
if (!tb[PACKAGE_URL]) {
|
|
result_send_error(1, "invalid parameters: missing package url for installation", 1, id);
|
|
return;
|
|
}
|
|
|
|
// Validate URL scheme (http or https)
|
|
const char *url = blobmsg_get_string(tb[PACKAGE_URL]);
|
|
if (!url || (strncmp(url, "http://", 7) != 0 && strncmp(url, "https://", 8) != 0)) {
|
|
result_send_error(1, "invalid parameters: package url must start with http:// or https://", 1, id);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!strcmp(op, "delete")) {
|
|
if (!tb[PACKAGE_NAME]) {
|
|
result_send_error(1, "invalid parameters: missing package name for removal", 1, id);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ULOG_DBG("Processing package: name=%s, url=%s\n", blobmsg_get_string(tb[PACKAGE_NAME]), blobmsg_get_string(tb[PACKAGE_URL]));
|
|
}
|
|
|
|
void *m, *s, *p;
|
|
m = result_new_blob(id, uuid_active);
|
|
s = blobmsg_open_table(&result, "status");
|
|
p = blobmsg_open_array(&result, "packages");
|
|
|
|
blobmsg_for_each_attr(cur, tb_root[ROOT_PACKAGES], rem) {
|
|
blobmsg_parse(package_policy, __PACKAGE_MAX, tb, blobmsg_data(cur), blobmsg_data_len(cur));
|
|
|
|
const char *pkg_name = blobmsg_get_string(tb[PACKAGE_NAME]);
|
|
const char *result_str = NULL;
|
|
void *pkg = blobmsg_open_table(&result, NULL);
|
|
|
|
if (!strcmp(op, "install")) {
|
|
const char *pkg_url = blobmsg_get_string(tb[PACKAGE_URL]);
|
|
result_str = cpm_install(pkg_name, pkg_url);
|
|
} else if (!strcmp(op, "delete")) {
|
|
result_str = cpm_remove(pkg_name);
|
|
}
|
|
|
|
blobmsg_add_string(&result, "name", pkg_name);
|
|
blobmsg_add_string(&result, "result", result_str);
|
|
if (strcmp(result_str, "Success") != 0) {
|
|
error_count++;
|
|
}
|
|
blobmsg_close_table(&result, pkg);
|
|
}
|
|
|
|
blobmsg_close_array(&result, p);
|
|
blobmsg_add_u32(&result, "error", error_count);
|
|
blobmsg_add_string(&result, "text", error_count ? "Some operations failed" : "Success");
|
|
blobmsg_close_table(&result, s);
|
|
blobmsg_close_table(&result, m);
|
|
result_send_blob();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*static void
|
|
transfer_handle(struct blob_attr **rpc)
|
|
{
|
|
enum {
|
|
TRANSFER_SERVER,
|
|
TRANSFER_PORT,
|
|
__TRANSFER_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy transfer_policy[__TRANSFER_MAX] = {
|
|
[TRANSFER_SERVER] = { .name = "server", .type = BLOBMSG_TYPE_STRING },
|
|
[TRANSFER_PORT] = { .name = "port", .type = BLOBMSG_TYPE_INT32 },
|
|
};
|
|
|
|
struct blob_attr *tb[__TRANSFER_MAX] = {};
|
|
uint32_t id = 0;
|
|
char *gateway;
|
|
FILE *fp;
|
|
|
|
blobmsg_parse(transfer_policy, __TRANSFER_MAX, tb, blobmsg_data(rpc[JSONRPC_PARAMS]),
|
|
blobmsg_data_len(rpc[JSONRPC_PARAMS]));
|
|
|
|
if (rpc[JSONRPC_ID])
|
|
id = blobmsg_get_u32(rpc[JSONRPC_ID]);
|
|
|
|
if (!tb[TRANSFER_SERVER] || !tb[TRANSFER_PORT]) {
|
|
result_send_error(1, "invalid parameters", 1, id);
|
|
return;
|
|
}
|
|
|
|
fp = fopen("/etc/ucentral/gateway.json", "w+");
|
|
if (!fp) {
|
|
configure_reply(1, "failed to store the new gateway", 0, id);
|
|
return;
|
|
}
|
|
gateway = blobmsg_format_json(rpc[JSONRPC_PARAMS], true);
|
|
if (!gateway) {
|
|
fclose(fp);
|
|
configure_reply(1, "failed to store the new gateway", 0, id);
|
|
return;
|
|
}
|
|
fprintf(fp, "%s", gateway);
|
|
free(gateway);
|
|
fclose(fp);
|
|
result_send_error(0, "success", 0, id);
|
|
}*/
|
|
|
|
static void
|
|
proto_handle_blob(void)
|
|
{
|
|
struct blob_attr *rpc[__JSONRPC_MAX] = {};
|
|
char *method;
|
|
|
|
blobmsg_parse(jsonrpc_policy, __JSONRPC_MAX, rpc, blob_data(proto.head), blob_len(proto.head));
|
|
if (rpc[JSONRPC_RADIUS]) {
|
|
ubus_forward_radius(&proto);
|
|
return;
|
|
}
|
|
|
|
if (!rpc[JSONRPC_VER] || (!rpc[JSONRPC_METHOD] && !rpc[JSONRPC_ERROR]) ||
|
|
(rpc[JSONRPC_METHOD] && !rpc[JSONRPC_PARAMS]) ||
|
|
strcmp(blobmsg_get_string(rpc[JSONRPC_VER]), "2.0")) {
|
|
log_send("received invalid jsonrpc call", LOG_ERR);
|
|
return;
|
|
}
|
|
|
|
if (rpc[JSONRPC_METHOD]) {
|
|
method = blobmsg_get_string(rpc[JSONRPC_METHOD]);
|
|
|
|
if (!strcmp(method, "configure"))
|
|
configure_handle(rpc);
|
|
else if (!strcmp(method, "ping"))
|
|
ping_handle(rpc);
|
|
else if (!strcmp(method, "reboot") ||
|
|
!strcmp(method, "transfer") ||
|
|
!strcmp(method, "factory"))
|
|
action_handle(rpc, method, 1, 10, 1, 0);
|
|
else if (!strcmp(method, "upgrade"))
|
|
action_handle(rpc, method, 0, 10, 1, 0);
|
|
else if (!strcmp(method, "perform") ||
|
|
!strcmp(method, "rtty") ||
|
|
!strcmp(method, "script") ||
|
|
!strcmp(method, "rrm") ||
|
|
!strcmp(method, "fixedconfig") ||
|
|
!strcmp(method, "fingerprint") ||
|
|
!strcmp(method, "reenroll") ||
|
|
!strcmp(method, "trace"))
|
|
action_handle(rpc, method, 0, 1, 0, 0);
|
|
else if (!strcmp(method, "wifiscan"))
|
|
action_handle(rpc, method, 0, 1, 0, 120);
|
|
else if (!strcmp(method, "leds"))
|
|
leds_handle(rpc);
|
|
else if (!strcmp(method, "request"))
|
|
request_handle(rpc);
|
|
else if (!strcmp(method, "event"))
|
|
event_handle(rpc);
|
|
else if (!strcmp(method, "telemetry"))
|
|
telemetry_handle(rpc);
|
|
else if (!strcmp(method, "venue_broadcast"))
|
|
venue_broadcast_handle(rpc[JSONRPC_PARAMS]);
|
|
else if (!strcmp(method, "package"))
|
|
package_handle(rpc);
|
|
}
|
|
|
|
if (rpc[JSONRPC_ERROR])
|
|
error_handle(rpc);
|
|
|
|
}
|
|
|
|
void
|
|
proto_handle(char *cmd)
|
|
{
|
|
ULOG_DBG("RX: %s\n", cmd);
|
|
|
|
blob_buf_init(&proto, 0);
|
|
if (!blobmsg_add_json_from_string(&proto, cmd)) {
|
|
log_send("failed to parse command", LOG_CRIT);
|
|
return;
|
|
}
|
|
proto_handle_blob();
|
|
}
|
|
|
|
void
|
|
proto_handle_simulate(struct blob_attr *a)
|
|
{
|
|
struct blob_attr *b;
|
|
char *msg;
|
|
size_t rem;
|
|
|
|
blob_buf_init(&proto, 0);
|
|
blobmsg_for_each_attr(b, a, rem)
|
|
blobmsg_add_blob(&proto, b);
|
|
msg = blobmsg_format_json(proto.head, true);
|
|
ULOG_DBG("RX: %s\n", msg);
|
|
free(msg);
|
|
proto_handle_blob();
|
|
|
|
}
|
|
|
|
void
|
|
proto_free(void)
|
|
{
|
|
blob_buf_free(&proto);
|
|
blob_buf_free(&result);
|
|
blob_buf_free(&action);
|
|
}
|