ACLK Passwd endpoint update (#10859)
Updates ACLK-NG to properly handle new response of password payload as defined in New Cloud Architecture
Timotej S committed
Apr 21, 2021 at 14:23 UTC
f9fbde67a4fad334806e78866acd6a22ad1f350b
6 files changed
+319
-76
aclk/aclk.c
+26
-11
@@ -201,6 +201,11 @@ static void msg_callback(const char *topic, const void *msg, size_t msglen, int
201
{
202
char cmsg[RX_MSGLEN_MAX];
203
size_t len = (msglen < RX_MSGLEN_MAX - 1) ? msglen : (RX_MSGLEN_MAX - 1);
204
+ const char *cmd_topic = aclk_get_topic(ACLK_TOPICID_COMMAND);
205
+ if (!cmd_topic) {
206
+ error("Error retrieving command topic");
207
+ return;
208
+ }
209
210
if (msglen > RX_MSGLEN_MAX - 1)
211
error("Incoming ACLK message was bigger than MAX of %d and got truncated.", RX_MSGLEN_MAX);
@@ -224,7 +229,7 @@ static void msg_callback(const char *topic, const void *msg, size_t msglen, int
229
230
debug(D_ACLK, "Got Message From Broker Topic \"%s\" QOS %d MSG: \"%s\"", topic, qos, cmsg);
231
227
- if (strcmp(aclk_get_topic(ACLK_TOPICID_COMMAND), topic))
232
+ if (strcmp(cmd_topic, topic))
233
error("Received message on unexpected topic %s", topic);
234
235
if (aclk_shared_state.mqtt_shutdown_msg_id > 0) {
@@ -323,7 +328,12 @@ static inline void mqtt_connected_actions(mqtt_wss_client client)
328
aclk_session_sec = now / USEC_PER_SEC;
329
aclk_session_us = now % USEC_PER_SEC;
330
326
- mqtt_wss_subscribe(client, aclk_get_topic(ACLK_TOPICID_COMMAND), 1);
331
+ const char *topic = aclk_get_topic(ACLK_TOPICID_COMMAND);
332
+
333
+ if (!topic)
334
+ error("Unable to fetch topic for COMMAND (to subscribe)");
335
+ else
336
+ mqtt_wss_subscribe(client, topic, 1);
337
338
aclk_stats_upd_online(1);
339
aclk_connected = 1;
@@ -331,7 +341,7 @@ static inline void mqtt_connected_actions(mqtt_wss_client client)
341
aclk_hello_msg(client);
342
ACLK_SHARED_STATE_LOCK;
343
if (aclk_shared_state.agent_state != AGENT_INITIALIZING) {
334
- error("Sending `connect` payload immediatelly as popcorning was finished already.");
344
+ error("Sending `connect` payload immediately as popcorning was finished already.");
345
queue_connect_payloads();
346
}
347
ACLK_SHARED_STATE_UNLOCK;
@@ -461,9 +471,6 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
471
#ifndef ACLK_DISABLE_CHALLENGE
472
url_t auth_url;
473
url_t mqtt_url;
464
-
465
- char *mqtt_otp_user = NULL;
466
- char *mqtt_otp_pass = NULL;
474
#endif
475
476
json_object *lwt;
@@ -494,7 +501,7 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
501
.clientid = "anon",
502
.username = "anon",
503
.password = "anon",
497
- .will_topic = aclk_get_topic(ACLK_TOPICID_METADATA),
504
+ .will_topic = "lwt",
505
.will_msg = NULL,
506
.will_flags = MQTT_WSS_PUB_QOS2,
507
.keep_alive = 60
@@ -522,16 +529,20 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
529
continue;
530
}
531
525
- ret = aclk_get_mqtt_otp(aclk_private_key, &mqtt_otp_user, &mqtt_otp_pass, &auth_url);
532
+ ret = aclk_get_mqtt_otp(aclk_private_key, (char **)&mqtt_conn_params.clientid, (char **)&mqtt_conn_params.username, (char **)&mqtt_conn_params.password, &auth_url);
533
url_t_destroy(&auth_url);
534
if (ret) {
535
error("Error passing Challenge/Response to get OTP");
536
continue;
537
}
538
532
- mqtt_conn_params.clientid = mqtt_otp_user;
533
- mqtt_conn_params.username = mqtt_otp_user;
534
- mqtt_conn_params.password = mqtt_otp_pass;
539
+ // aclk_get_topic moved here as during OTP we
540
+ // generate the topic cache
541
+ mqtt_conn_params.will_topic = aclk_get_topic(ACLK_TOPICID_METADATA);
542
+ if (!mqtt_conn_params.will_topic) {
543
+ error("Couldn't get LWT topic. Will not send LWT.");
544
+ continue;
545
+ }
546
547
// Do the MQTT connection
548
ret = aclk_get_transport_idx(aclk_env);
@@ -558,6 +569,10 @@ static int aclk_attempt_to_connect(mqtt_wss_client client)
569
#else
570
ret = mqtt_wss_connect(client, mqtt_url.host, mqtt_url.port, &mqtt_conn_params, ACLK_SSL_FLAGS, &proxy_conf);
571
url_t_destroy(&mqtt_url);
572
+
573
+ freez((char*)mqtt_conn_params.clientid);
574
+ freez((char*)mqtt_conn_params.password);
575
+ freez((char*)mqtt_conn_params.username);
576
#endif
577
578
json_object_put(lwt);
aclk/aclk_otp.c
+96
-26
@@ -188,8 +188,95 @@ static int aclk_https_request(https_req_t *request, https_req_response_t *respon
188
return rc;
189
}
190
191
+struct auth_data {
192
+ char *client_id;
193
+ char *username;
194
+ char *passwd;
195
+};
196
+
197
+#define PARSE_ENV_JSON_CHK_TYPE(it, type, name) \
198
+ if (json_object_get_type(json_object_iter_peek_value(it)) != type) { \
199
+ error("value of key \"%s\" should be %s", name, #type); \
200
+ goto exit; \
201
+ }
202
+
203
+#define JSON_KEY_CLIENTID "clientID"
204
+#define JSON_KEY_USER "username"
205
+#define JSON_KEY_PASS "password"
206
+#define JSON_KEY_TOPICS "topics"
207
+
208
+static int parse_passwd_response(const char *json_str, struct auth_data *auth) {
209
+ int rc = 1;
210
+ json_object *json;
211
+ struct json_object_iterator it;
212
+ struct json_object_iterator itEnd;
213
+
214
+ json = json_tokener_parse(json_str);
215
+ if (!json) {
216
+ error("JSON-C failed to parse the payload of http respons of /env endpoint");
217
+ return 1;
218
+ }
219
+
220
+ it = json_object_iter_begin(json);
221
+ itEnd = json_object_iter_end(json);
222
+
223
+ while (!json_object_iter_equal(&it, &itEnd)) {
224
+ if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_CLIENTID)) {
225
+ PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_CLIENTID)
226
+
227
+ auth->client_id = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
228
+ json_object_iter_next(&it);
229
+ continue;
230
+ }
231
+ if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_USER)) {
232
+ PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_USER)
233
+
234
+ auth->username = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
235
+ json_object_iter_next(&it);
236
+ continue;
237
+ }
238
+ if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_PASS)) {
239
+ PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_PASS)
240
+
241
+ auth->passwd = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
242
+ json_object_iter_next(&it);
243
+ continue;
244
+ }
245
+ if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_TOPICS)) {
246
+ PARSE_ENV_JSON_CHK_TYPE(&it, json_type_array, JSON_KEY_TOPICS)
247
+
248
+ if (aclk_generate_topic_cache(json_object_iter_peek_value(&it))) {
249
+ error("Failed to generate topic cache!");
250
+ goto exit;
251
+ }
252
+ json_object_iter_next(&it);
253
+ continue;
254
+ }
255
+ error("Unknown key \"%s\" in passwd response payload. Ignoring", json_object_iter_peek_name(&it));
256
+ json_object_iter_next(&it);
257
+ }
258
+
259
+ if (!auth->client_id) {
260
+ error(JSON_KEY_CLIENTID " is compulsory key in /password response");
261
+ goto exit;
262
+ }
263
+ if (!auth->passwd) {
264
+ error(JSON_KEY_PASS " is compulsory in /password response");
265
+ goto exit;
266
+ }
267
+ if (!auth->username) {
268
+ error(JSON_KEY_USER " is compulsory in /password response");
269
+ goto exit;
270
+ }
271
+
272
+ rc = 0;
273
+exit:
274
+ json_object_put(json);
275
+ return rc;
276
+}
277
+
278
#define OTP_URL_PREFIX "/api/v1/auth/node/"
192
-int aclk_get_mqtt_otp(RSA *p_key, char **mqtt_usr, char **mqtt_pass, url_t *target) {
279
+int aclk_get_mqtt_otp(RSA *p_key, char **mqtt_id, char **mqtt_usr, char **mqtt_pass, url_t *target) {
280
// TODO this fnc will be rewritten and simplified in following PRs
281
// still carries lot of baggage from ACLK Legacy
282
int rc = 1;
@@ -272,43 +359,26 @@ int aclk_get_mqtt_otp(RSA *p_key, char **mqtt_usr, char **mqtt_pass, url_t *targ
359
}
360
info ("ACLK_OTP Got Password from Cloud");
361
275
- struct dictionary_singleton password = { .key = "password", .result = NULL };
276
- if (json_parse(resp.payload, &password, json_extract_singleton) != JSON_OK)
277
- {
278
- freez(password.result);
279
- error("Could not parse the json response with the password");
362
+ struct auth_data data = { .client_id = NULL, .passwd = NULL, .username = NULL };
363
+
364
+ if (parse_passwd_response(resp.payload, &data)){
365
+ error("Error parsing response of password endpoint");
366
goto cleanup_resp;
367
}
368
283
- if (password.result == NULL ) {
284
- error("Could not retrieve password from auth response");
285
- goto cleanup_resp;
286
- }
287
- if (*mqtt_pass != NULL )
288
- freez(*mqtt_pass);
289
- *mqtt_pass = password.result;
290
- if (*mqtt_usr != NULL)
291
- freez(*mqtt_usr);
292
- *mqtt_usr = agent_id;
293
- agent_id = NULL;
369
+ *mqtt_pass = data.passwd;
370
+ *mqtt_usr = data.username;
371
+ *mqtt_id = data.client_id;
372
373
rc = 0;
296
-
374
cleanup_resp:
375
https_req_response_free(&resp);
376
cleanup:
300
- if (agent_id != NULL)
301
- freez(agent_id);
377
+ freez(agent_id);
378
buffer_free(url);
379
return rc;
380
}
381
306
-#define PARSE_ENV_JSON_CHK_TYPE(it, type, name) \
307
- if (json_object_get_type(json_object_iter_peek_value(it)) != type) { \
308
- error("value of key \"%s\" should be %s", name, #type); \
309
- goto exit; \
310
- }
311
-
382
#define JSON_KEY_ENC "encoding"
383
#define JSON_KEY_AUTH_ENDPOINT "authEndpoint"
384
#define JSON_KEY_TRP "transports"
aclk/aclk_otp.h
+1
-1
@@ -7,7 +7,7 @@
7
8
#include "https_client.h"
9
10
-int aclk_get_mqtt_otp(RSA *p_key, char **mqtt_usr, char **mqtt_pass, url_t *target);
10
+int aclk_get_mqtt_otp(RSA *p_key, char **mqtt_id, char **mqtt_usr, char **mqtt_pass, url_t *target);
11
int aclk_get_env(aclk_env_t *env, const char *aclk_hostname, int aclk_port);
12
13
#endif /* ACLK_OTP_H */
aclk/aclk_tx_msgs.c
+14
-2
@@ -13,8 +13,14 @@ static void aclk_send_message_subtopic(mqtt_wss_client client, json_object *msg,
13
{
14
uint16_t packet_id;
15
const char *str = json_object_to_json_string_ext(msg, JSON_C_TO_STRING_PLAIN);
16
+ const char *topic = aclk_get_topic(subtopic);
17
17
- mqtt_wss_publish_pid(client, aclk_get_topic(subtopic), str, strlen(str), MQTT_WSS_PUB_QOS1, &packet_id);
18
+ if (unlikely(!topic)) {
19
+ error("Couldn't get topic. Aborting mesage send");
20
+ return;
21
+ }
22
+
23
+ mqtt_wss_publish_pid(client, topic, str, strlen(str), MQTT_WSS_PUB_QOS1, &packet_id);
24
#ifdef NETDATA_INTERNAL_CHECKS
25
aclk_stats_msg_published(packet_id);
26
#endif
@@ -30,8 +36,14 @@ static uint16_t aclk_send_message_subtopic_pid(mqtt_wss_client client, json_obje
36
{
37
uint16_t packet_id;
38
const char *str = json_object_to_json_string_ext(msg, JSON_C_TO_STRING_PLAIN);
39
+ const char *topic = aclk_get_topic(subtopic);
40
+
41
+ if (unlikely(!topic)) {
42
+ error("Couldn't get topic. Aborting mesage send");
43
+ return 0;
44
+ }
45
34
- mqtt_wss_publish_pid(client, aclk_get_topic(subtopic), str, strlen(str), MQTT_WSS_PUB_QOS1, &packet_id);
46
+ mqtt_wss_publish_pid(client, topic, str, strlen(str), MQTT_WSS_PUB_QOS1, &packet_id);
47
#ifdef NETDATA_INTERNAL_CHECKS
48
aclk_stats_msg_published(packet_id);
49
#endif
aclk/aclk_util.c
+176
-32
@@ -72,52 +72,188 @@ int aclk_get_conv_log_next()
72
#define ACLK_TOPIC_PREFIX "/agent/"
73
74
struct aclk_topic {
75
- const char *topic_suffix;
75
+ enum aclk_topics topic_id;
76
+ // as received from cloud - we keep this for
77
+ // eventual topic list update when claim_id changes
78
+ char *topic_recvd;
79
+ // constructed topic
80
char *topic;
81
};
82
83
// This helps to cache finalized topics (assembled with claim_id)
84
// to not have to alloc or create buffer and construct topic every
85
// time message is sent as in old ACLK
82
-static struct aclk_topic aclk_topic_cache[] = {
83
- { .topic_suffix = "outbound/meta", .topic = NULL }, // ACLK_TOPICID_CHART
84
- { .topic_suffix = "outbound/alarms", .topic = NULL }, // ACLK_TOPICID_ALARMS
85
- { .topic_suffix = "outbound/meta", .topic = NULL }, // ACLK_TOPICID_METADATA
86
- { .topic_suffix = "inbound/cmd", .topic = NULL }, // ACLK_TOPICID_COMMAND
87
- { .topic_suffix = NULL, .topic = NULL }
88
-};
86
+static struct aclk_topic **aclk_topic_cache = NULL;
87
+static size_t aclk_topic_cache_items = 0;
88
89
void free_topic_cache(void)
90
{
92
- struct aclk_topic *tc = aclk_topic_cache;
93
- while (tc->topic_suffix) {
94
- if (tc->topic) {
95
- freez(tc->topic);
96
- tc->topic = NULL;
91
+ if (aclk_topic_cache) {
92
+ for (size_t i = 0; i < aclk_topic_cache_items; i++) {
93
+ freez(aclk_topic_cache[i]->topic);
94
+ freez(aclk_topic_cache[i]->topic_recvd);
95
+ freez(aclk_topic_cache[i]);
96
}
98
- tc++;
97
+ freez(aclk_topic_cache);
98
+ aclk_topic_cache = NULL;
99
+ aclk_topic_cache_items = 0;
100
}
101
}
102
102
-static inline void generate_topic_cache(void)
103
-{
104
- struct aclk_topic *tc = aclk_topic_cache;
105
- char *ptr;
106
- if (unlikely(!tc->topic)) {
107
- rrdhost_aclk_state_lock(localhost);
108
- while(tc->topic_suffix) {
109
- tc->topic = mallocz(strlen(ACLK_TOPIC_PREFIX) + (UUID_STR_LEN - 1) + 2 /* '/' and \0 */ + strlen(tc->topic_suffix));
110
- ptr = tc->topic;
111
- strcpy(ptr, ACLK_TOPIC_PREFIX);
112
- ptr += strlen(ACLK_TOPIC_PREFIX);
113
- strcpy(ptr, localhost->aclk_state.claimed_id);
114
- ptr += (UUID_STR_LEN - 1);
115
- *ptr++ = '/';
116
- strcpy(ptr, tc->topic_suffix);
117
- tc++;
103
+#define JSON_TOPIC_KEY_TOPIC "topic"
104
+#define JSON_TOPIC_KEY_NAME "name"
105
+
106
+struct topic_name {
107
+ enum aclk_topics id;
108
+ // cloud name - how is it called
109
+ // in answer to /password endpoint
110
+ const char *name;
111
+} topic_names[] = {
112
+ { .id = ACLK_TOPICID_CHART, .name = "chart" },
113
+ { .id = ACLK_TOPICID_ALARMS, .name = "alarms" },
114
+ { .id = ACLK_TOPICID_METADATA, .name = "meta" },
115
+ { .id = ACLK_TOPICID_COMMAND, .name = "inbox-cmd" },
116
+ { .id = ACLK_TOPICID_UNKNOWN, .name = NULL }
117
+};
118
+
119
+enum aclk_topics compulsory_topics[] = {
120
+ ACLK_TOPICID_CHART,
121
+ ACLK_TOPICID_ALARMS,
122
+ ACLK_TOPICID_METADATA,
123
+ ACLK_TOPICID_COMMAND,
124
+ ACLK_TOPICID_UNKNOWN
125
+};
126
+
127
+static enum aclk_topics topic_name_to_id(const char *name) {
128
+ struct topic_name *topic = topic_names;
129
+ while (topic->name) {
130
+ if (!strcmp(topic->name, name)) {
131
+ return topic->id;
132
}
133
+ topic++;
134
+ }
135
+ return ACLK_TOPICID_UNKNOWN;
136
+}
137
+
138
+static const char *topic_id_to_name(enum aclk_topics tid) {
139
+ struct topic_name *topic = topic_names;
140
+ while (topic->name) {
141
+ if (topic->id == tid)
142
+ return topic->name;
143
+ topic++;
144
+ }
145
+ return "unknown";
146
+}
147
+
148
+#define CLAIM_ID_REPLACE_TAG "#{claim_id}"
149
+static void topic_generate_final(struct aclk_topic *t) {
150
+ char *dest;
151
+ char *replace_tag = strstr(t->topic_recvd, CLAIM_ID_REPLACE_TAG);
152
+ if (!replace_tag)
153
+ return;
154
+
155
+ rrdhost_aclk_state_lock(localhost);
156
+ if (unlikely(!localhost->aclk_state.claimed_id)) {
157
+ error("This should never be called if agent not claimed");
158
rrdhost_aclk_state_unlock(localhost);
159
+ return;
160
}
161
+
162
+ t->topic = mallocz(strlen(t->topic_recvd) + 1 - strlen(CLAIM_ID_REPLACE_TAG) + strlen(localhost->aclk_state.claimed_id));
163
+ memcpy(t->topic, t->topic_recvd, replace_tag - t->topic_recvd);
164
+ dest = t->topic + (replace_tag - t->topic_recvd);
165
+
166
+ memcpy(dest, localhost->aclk_state.claimed_id, strlen(localhost->aclk_state.claimed_id));
167
+ dest += strlen(localhost->aclk_state.claimed_id);
168
+ rrdhost_aclk_state_unlock(localhost);
169
+ replace_tag += strlen(CLAIM_ID_REPLACE_TAG);
170
+ strcpy(dest, replace_tag);
171
+ dest += strlen(replace_tag);
172
+ *dest = 0;
173
+}
174
+
175
+static int topic_cache_add_topic(struct json_object *json, struct aclk_topic *topic)
176
+{
177
+ struct json_object_iterator it;
178
+ struct json_object_iterator itEnd;
179
+
180
+ it = json_object_iter_begin(json);
181
+ itEnd = json_object_iter_end(json);
182
+
183
+ while (!json_object_iter_equal(&it, &itEnd)) {
184
+ if (!strcmp(json_object_iter_peek_name(&it), JSON_TOPIC_KEY_NAME)) {
185
+ if (json_object_get_type(json_object_iter_peek_value(&it)) != json_type_string) {
186
+ error("topic dictionary key \"" JSON_TOPIC_KEY_NAME "\" is expected to be json_type_string");
187
+ return 1;
188
+ }
189
+ topic->topic_id = topic_name_to_id(json_object_get_string(json_object_iter_peek_value(&it)));
190
+ if (topic->topic_id == ACLK_TOPICID_UNKNOWN) {
191
+ info("topic dictionary has unkown topic name \"%s\"", json_object_get_string(json_object_iter_peek_value(&it)));
192
+ }
193
+ json_object_iter_next(&it);
194
+ continue;
195
+ }
196
+ if (!strcmp(json_object_iter_peek_name(&it), JSON_TOPIC_KEY_TOPIC)) {
197
+ if (json_object_get_type(json_object_iter_peek_value(&it)) != json_type_string) {
198
+ error("topic dictionary key \"" JSON_TOPIC_KEY_TOPIC "\" is expected to be json_type_string");
199
+ return 1;
200
+ }
201
+ topic->topic_recvd = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
202
+ json_object_iter_next(&it);
203
+ continue;
204
+ }
205
+
206
+ error("topic dictionary has Unknown/Unexpected key \"%s\" in topic description. Ignoring!", json_object_iter_peek_name(&it));
207
+ json_object_iter_next(&it);
208
+ }
209
+
210
+ if (!topic->topic_recvd) {
211
+ error("topic dictionary Missig compulsory key %s", JSON_TOPIC_KEY_TOPIC);
212
+ return 1;
213
+ }
214
+
215
+ topic_generate_final(topic);
216
+ aclk_topic_cache_items++;
217
+
218
+ return 0;
219
+}
220
+
221
+int aclk_generate_topic_cache(struct json_object *json)
222
+{
223
+ json_object *obj;
224
+
225
+ size_t array_size = json_object_array_length(json);
226
+ if (!array_size) {
227
+ error("Empty topic list!");
228
+ return 1;
229
+ }
230
+
231
+ if (aclk_topic_cache)
232
+ free_topic_cache();
233
+
234
+ aclk_topic_cache = callocz(array_size, sizeof(struct aclk_topic *));
235
+
236
+ for (size_t i = 0; i < array_size; i++) {
237
+ obj = json_object_array_get_idx(json, i);
238
+ if (json_object_get_type(obj) != json_type_object) {
239
+ error("expected json_type_object");
240
+ return 1;
241
+ }
242
+ aclk_topic_cache[i] = callocz(1, sizeof(struct aclk_topic));
243
+ if (topic_cache_add_topic(obj, aclk_topic_cache[i])) {
244
+ error("failed to parse topic @idx=%d", (int)i);
245
+ return 1;
246
+ }
247
+ }
248
+
249
+ for (int i = 0; compulsory_topics[i] != ACLK_TOPICID_UNKNOWN; i++) {
250
+ if (!aclk_get_topic(compulsory_topics[i])) {
251
+ error("missing compulsory topic \"%s\" in password response from cloud", topic_id_to_name(compulsory_topics[i]));
252
+ return 1;
253
+ }
254
+ }
255
+
256
+ return 0;
257
}
258
259
/*
@@ -127,9 +263,17 @@ static inline void generate_topic_cache(void)
263
*/
264
const char *aclk_get_topic(enum aclk_topics topic)
265
{
130
- generate_topic_cache();
266
+ if (!aclk_topic_cache) {
267
+ error("Topic cache not initialized");
268
+ return NULL;
269
+ }
270
132
- return aclk_topic_cache[topic].topic;
271
+ for (size_t i = 0; i < aclk_topic_cache_items; i++) {
272
+ if (aclk_topic_cache[i]->topic_id == topic)
273
+ return aclk_topic_cache[i]->topic;
274
+ }
275
+ error("Unknown topic");
276
+ return NULL;
277
}
278
279
/*
aclk/aclk_util.h
+6
-4
@@ -51,13 +51,15 @@ void aclk_transport_desc_t_destroy(aclk_transport_desc_t *trp_desc);
51
void aclk_env_t_destroy(aclk_env_t *env);
52
53
enum aclk_topics {
54
- ACLK_TOPICID_CHART = 0,
55
- ACLK_TOPICID_ALARMS = 1,
56
- ACLK_TOPICID_METADATA = 2,
57
- ACLK_TOPICID_COMMAND = 3,
54
+ ACLK_TOPICID_UNKNOWN = 0,
55
+ ACLK_TOPICID_CHART = 1,
56
+ ACLK_TOPICID_ALARMS = 2,
57
+ ACLK_TOPICID_METADATA = 3,
58
+ ACLK_TOPICID_COMMAND = 4
59
};
60
61
const char *aclk_get_topic(enum aclk_topics topic);
62
+int aclk_generate_topic_cache(struct json_object *json);
63
void free_topic_cache(void);
64
// TODO
65
// aclk_topics_reload //when claim id changes