Files
wlan-ucentral-client/ubus.c
John Crispin 8c45f965c3 send on/offline events to cloud discovery service
Signed-off-by: John Crispin <john@phrozen.org>
2024-11-12 10:35:52 +01:00

466 lines
12 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause */
#include "ucentral.h"
#include <libubus.h>
static struct ubus_auto_conn conn;
static struct blob_buf u;
static uint32_t radius_proxy;
static uint32_t state;
static int ubus_status_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
struct timespec tp;
time_t delta;
clock_gettime(CLOCK_MONOTONIC, &tp);
delta = tp.tv_sec - conn_time;
blob_buf_init(&u, 0);
blobmsg_add_u32(&u, websocket ? "connected" : "disconnected", delta);
if (!websocket)
blobmsg_add_u32(&u, "reconnect", reconnect_time - delta);
blobmsg_add_u32(&u, "latest", uuid_latest);
blobmsg_add_u32(&u, "active", uuid_active);
ubus_send_reply(ctx, req, u.head);
return UBUS_STATUS_OK;
}
static int ubus_send_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
if (!msg)
return UBUS_STATUS_INVALID_ARGUMENT;
raw_send(msg);
return UBUS_STATUS_OK;
}
static int ubus_venue_broadcast_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
if (!msg)
return UBUS_STATUS_INVALID_ARGUMENT;
venue_broadcast_send(msg);
return UBUS_STATUS_OK;
}
static int ubus_radius_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
if (!msg)
return UBUS_STATUS_INVALID_ARGUMENT;
radius_send(msg);
return UBUS_STATUS_OK;
}
enum {
LOG_MSG,
LOG_SEVERITY,
__LOG_MAX,
};
static const struct blobmsg_policy log_policy[__LOG_MAX] = {
[LOG_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_STRING },
[LOG_SEVERITY] = { .name = "severity", .type = BLOBMSG_TYPE_INT32 },
};
static int ubus_log_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
struct blob_attr *tb[__LOG_MAX] = {};
int severity = LOG_INFO;
blobmsg_parse(log_policy, __LOG_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
if (!tb[LOG_MSG])
return UBUS_STATUS_INVALID_ARGUMENT;
if (tb[LOG_SEVERITY])
severity = blobmsg_get_u32(tb[LOG_SEVERITY]);
log_send(blobmsg_get_string(tb[LOG_MSG]), severity);
return UBUS_STATUS_OK;
}
enum {
HEALTH_SANITY,
HEALTH_DATA,
__HEALTH_MAX,
};
static const struct blobmsg_policy health_policy[__HEALTH_MAX] = {
[HEALTH_SANITY] = { .name = "sanity", .type = BLOBMSG_TYPE_INT32 },
[HEALTH_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
};
static int ubus_health_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
struct blob_attr *tb[__HEALTH_MAX] = {};
blobmsg_parse(health_policy, __HEALTH_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
if (!tb[HEALTH_SANITY] || !tb[HEALTH_DATA])
return UBUS_STATUS_INVALID_ARGUMENT;
health_send(blobmsg_get_u32(tb[HEALTH_SANITY]), tb[HEALTH_DATA]);
return UBUS_STATUS_OK;
}
static int ubus_simulate_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
proto_handle_simulate(msg);
return UBUS_STATUS_OK;
}
enum {
RESULT_STATUS,
RESULT_UUID,
RESULT_ID,
__RESULT_MAX,
};
static const struct blobmsg_policy result_policy[__RESULT_MAX] = {
[RESULT_STATUS] = { .name = "status", .type = BLOBMSG_TYPE_TABLE },
[RESULT_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_INT32 },
[RESULT_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
};
static int ubus_result_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
struct blob_attr *tb[__RESULT_MAX] = {};
time_t uuid = 0;
blobmsg_parse(result_policy, __RESULT_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
if (!tb[RESULT_STATUS] || !tb[RESULT_ID])
return UBUS_STATUS_INVALID_ARGUMENT;
if (tb[RESULT_UUID])
uuid = blobmsg_get_u32(tb[RESULT_UUID]);
result_send(blobmsg_get_u32(tb[RESULT_ID]), tb[RESULT_STATUS], uuid);
return UBUS_STATUS_OK;
}
static int ubus_stats_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
stats_send(msg);
return UBUS_STATUS_OK;
}
static int ubus_upload_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
upload_run(msg);
return UBUS_STATUS_OK;
}
static int ubus_ip_collide_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
ip_collide_run();
return UBUS_STATUS_OK;
}
static int ubus_rejected_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
config_rejected(msg);
return UBUS_STATUS_OK;
}
static int ubus_event_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
event_add(msg);
return UBUS_STATUS_OK;
}
enum {
TELEMETRY_EVENT,
TELEMETRY_PAYLOAD,
TELEMETRY_DUMP,
TELEMETRY_TYPE,
__TELEMETRY_MAX,
};
static const struct blobmsg_policy telemetry_policy[__TELEMETRY_MAX] = {
[TELEMETRY_EVENT] = { .name = "event", .type = BLOBMSG_TYPE_STRING },
[TELEMETRY_PAYLOAD] = { .name = "payload", .type = BLOBMSG_TYPE_TABLE },
[TELEMETRY_DUMP] = { .name = "dump", .type = BLOBMSG_TYPE_BOOL },
[TELEMETRY_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
};
static int ubus_telemetry_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
struct blob_attr *tb[__TELEMETRY_MAX] = {};
blobmsg_parse(telemetry_policy, __TELEMETRY_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
if (tb[TELEMETRY_DUMP] && tb[TELEMETRY_TYPE]) {
blob_buf_init(&u, 0);
event_dump(&u, blobmsg_get_string(tb[TELEMETRY_TYPE]), false);
ubus_send_reply(ctx, req, u.head);
return UBUS_STATUS_OK;
}
if (tb[TELEMETRY_DUMP]) {
blob_buf_init(&u, 0);
event_dump_all(&u);
ubus_send_reply(ctx, req, u.head);
return UBUS_STATUS_OK;
}
if (!tb[TELEMETRY_EVENT] || !tb[TELEMETRY_PAYLOAD])
return UBUS_STATUS_INVALID_ARGUMENT;
telemetry_add(blobmsg_get_string(tb[TELEMETRY_EVENT]), tb[TELEMETRY_PAYLOAD]);
return UBUS_STATUS_OK;
}
enum {
CONFIG_TELEMETRY,
__CONFIG_MAX,
};
static const struct blobmsg_policy config_policy[__CONFIG_MAX] = {
[CONFIG_TELEMETRY] = { .name = "telemetry", .type = BLOBMSG_TYPE_INT32 },
};
static int ubus_config_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
struct blob_attr *tb[__CONFIG_MAX] = {};
blobmsg_parse(config_policy, __CONFIG_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
if (tb[CONFIG_TELEMETRY])
client.telemetry_interval = blobmsg_get_u32(tb[CONFIG_TELEMETRY]);
event_config();
return UBUS_STATUS_OK;
}
enum {
PWD_PASSWORD,
__PWD_MAX,
};
static const struct blobmsg_policy password_policy[__PWD_MAX] = {
[PWD_PASSWORD] = { .name = "passwd", .type = BLOBMSG_TYPE_STRING },
};
static int ubus_password_cb(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
struct blob_attr *tb[__PWD_MAX] = {};
blobmsg_parse(password_policy, __PWD_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
if (!tb[PWD_PASSWORD])
return UBUS_STATUS_INVALID_ARGUMENT;
password_notify(blobmsg_get_string(tb[PWD_PASSWORD]));
return UBUS_STATUS_OK;
}
void ubus_forward_radius(struct blob_buf *msg)
{
struct ubus_request async = { };
if (!radius_proxy) {
ULOG_ERR("radius_proxy is not online\n");
return;
}
ubus_invoke_async(&conn.ctx, radius_proxy, "frame", msg->head, &async);
ubus_abort_request(&conn.ctx, &async);
}
void ubus_set_client_status(char *status)
{
struct ubus_request async = { };
if (!strcmp(status, "online"))
system("ubus call cloud online");
else if (!strcmp(status, "offline"))
system("ubus call cloud offline");
if (!state) {
ULOG_ERR("state is not running\n");
return;
}
blob_buf_init(&u, 0);
blobmsg_add_string(&u, "state", status);
ubus_invoke_async(&conn.ctx, state, "set", u.head, &async);
ubus_abort_request(&conn.ctx, &async);
}
void ubus_blink_leds(int duration)
{
struct ubus_request async = { };
if (!state) {
ULOG_ERR("state is not running\n");
return;
}
blob_buf_init(&u, 0);
blobmsg_add_string(&u, "state", "blink");
blobmsg_add_u32(&u, "duration", duration);
ubus_invoke_async(&conn.ctx, state, "set", u.head, &async);
ubus_abort_request(&conn.ctx, &async);
}
static const struct ubus_method ucentral_methods[] = {
UBUS_METHOD("health", ubus_health_cb, health_policy),
UBUS_METHOD("result", ubus_result_cb, result_policy),
UBUS_METHOD("log", ubus_log_cb, log_policy),
UBUS_METHOD("telemetry", ubus_telemetry_cb, telemetry_policy),
UBUS_METHOD("config", ubus_config_cb, config_policy),
UBUS_METHOD("password", ubus_password_cb, password_policy),
UBUS_METHOD_NOARG("venue_broadcast", ubus_venue_broadcast_cb),
UBUS_METHOD_NOARG("event", ubus_event_cb),
UBUS_METHOD_NOARG("status", ubus_status_cb),
UBUS_METHOD_NOARG("stats", ubus_stats_cb),
UBUS_METHOD_NOARG("send", ubus_send_cb),
UBUS_METHOD_NOARG("radius", ubus_radius_cb),
UBUS_METHOD_NOARG("simulate", ubus_simulate_cb),
UBUS_METHOD_NOARG("rejected", ubus_rejected_cb),
UBUS_METHOD_NOARG("upload", ubus_upload_cb),
UBUS_METHOD_NOARG("ip_collide", ubus_ip_collide_cb),
};
static struct ubus_object_type ubus_object_type =
UBUS_OBJECT_TYPE("ucentral", ucentral_methods);
struct ubus_object ubus_object = {
.name = "ucentral",
.type = &ubus_object_type,
.methods = ucentral_methods,
.n_methods = ARRAY_SIZE(ucentral_methods),
};
void venue_broadcast_handle(struct blob_attr *rpc)
{
if (!ubus_object.has_subscribers)
return;
ubus_notify(&conn.ctx, &ubus_object, "venue", rpc, -1);
}
static void
event_handler_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
const char *type, struct blob_attr *msg)
{
enum {
EVENT_ID,
EVENT_PATH,
__EVENT_MAX
};
static const struct blobmsg_policy status_policy[__EVENT_MAX] = {
[EVENT_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
[EVENT_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
};
struct blob_attr *tb[__EVENT_MAX];
char *path;
uint32_t id;
blobmsg_parse(status_policy, __EVENT_MAX, tb, blob_data(msg), blob_len(msg));
if (!tb[EVENT_ID] || !tb[EVENT_PATH])
return;
path = blobmsg_get_string(tb[EVENT_PATH]);
id = blobmsg_get_u32(tb[EVENT_ID]);
if (!strcmp("ubus.object.remove", type))
id = 0;
if (!strcmp(path, "radius.proxy"))
radius_proxy = id;
else if (!strcmp(path, "state"))
state = id;
else
return;
ULOG_INFO("%s %s (%d)\n", id ? "add" : "remove", path, id);
}
static struct ubus_event_handler event_handler = { .cb = event_handler_cb };
static void ubus_connect_handler(struct ubus_context *ctx)
{
ubus_add_object(ctx, &ubus_object);
ubus_register_event_handler(ctx, &event_handler, "ubus.object.add");
ubus_register_event_handler(ctx, &event_handler, "ubus.object.remove");
ubus_lookup_id(ctx, "radius.proxy", &radius_proxy);
ubus_lookup_id(ctx, "state", &state);
}
void ubus_init(void)
{
conn.cb = ubus_connect_handler;
ubus_auto_connect(&conn);
}
void ubus_deinit(void)
{
ubus_auto_shutdown(&conn);
}