1 /*
2 * aprsc
3 *
4 * (c) Heikki Hannikainen, OH7LZB <hessu@hes.iki.fi>
5 *
6 * This program is licensed under the BSD license, which can be found
7 * in the file LICENSE.
8 *
9 */
10
11 /*
12 * Generate the status JSON string for the web status view
13 */
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <sys/utsname.h>
20 #include <ctype.h>
21 #include <sys/stat.h>
22
23 #include "status.h"
24 #include "cellmalloc.h"
25 #include "hmalloc.h"
26 #include "config.h"
27 #include "version.h"
28 #include "hlog.h"
29 #include "worker.h"
30 #include "historydb.h"
31 #include "dupecheck.h"
32 #include "filter.h"
33 #include "incoming.h"
34 #include "accept.h"
35 #include "cJSON.h"
36 #include "counterdata.h"
37 #include "client_heard.h"
38
39 time_t startup_tick, startup_time;
40
41 pthread_mutex_t status_json_mt = PTHREAD_MUTEX_INITIALIZER;
42 char *status_json_cached = NULL;
43 time_t status_json_cache_t = 0;
44
45 struct cdata_list_t {
46 const char *tree;
47 const char *name;
48 struct cdata_list_t *next;
49 struct cdata_t *cd;
50 int gauge;
51 } *cdata_list = NULL;
52
53 cJSON *liveupgrade_status = NULL;
54
55 struct status_error_t {
56 struct status_error_t *next;
57 char *err;
58 int set;
59 time_t started;
60 time_t ends;
61 } *status_errs = NULL;
62
63 pthread_mutex_t status_errs_mt = PTHREAD_MUTEX_INITIALIZER;
64
65 /*
66 * Find an error entry, allocate new if one does not exist
67 */
68
status_error_find(const char * err)69 struct status_error_t *status_error_find(const char *err)
70 {
71 struct status_error_t *e;
72
73 for (e = status_errs; (e); e = e->next) {
74 if (strcmp(e->err, err) == 0)
75 return e;
76 }
77
78 e = hmalloc(sizeof(*e));
79 e->err = hstrdup(err);
80 e->next = status_errs;
81 e->set = -1;
82 status_errs = e;
83
84 return e;
85 }
86
87 /*
88 * Update error flag status
89 * ttl: seconds to expire the error in, or 0 to clear error now
90 */
91
status_error(int ttl,const char * err)92 void status_error(int ttl, const char *err)
93 {
94 struct status_error_t *e;
95 int pe;
96
97 if (ttl == -1)
98 hlog(LOG_INFO, "status: clearing error flag %s", err);
99 else
100 hlog(LOG_INFO, "status: setting error flag %s ttl %d", err, ttl);
101
102 if ((pe = pthread_mutex_lock(&status_errs_mt))) {
103 hlog(LOG_ERR, "status_error(): could not lock status_errs_mt: %s", strerror(pe));
104 return;
105 }
106
107 e = status_error_find(err);
108 if (ttl > 0) {
109 if (e->set != 1) {
110 e->started = time(NULL);
111 e->set = 1;
112 }
113
114 /* if the alarm is set already, just move the end time forward */
115 e->ends = e->started + ttl;
116 } else {
117 if (e->set != 0) {
118 e->ends = time(NULL);
119 e->set = 0;
120 }
121 }
122
123 if ((pe = pthread_mutex_unlock(&status_errs_mt))) {
124 hlog(LOG_ERR, "status_error(): could not unlock status_errs_mt: %s", strerror(pe));
125 return;
126 }
127 }
128
status_error_json(void)129 cJSON *status_error_json(void)
130 {
131 struct status_error_t *e;
132 int pe;
133 struct cJSON *ea;
134
135 if ((pe = pthread_mutex_lock(&status_errs_mt))) {
136 hlog(LOG_ERR, "status_error_json(): could not lock status_errs_mt: %s", strerror(pe));
137 return NULL;
138 }
139
140 ea = cJSON_CreateArray();
141
142 for (e = status_errs; (e); e = e->next) {
143 if (e->ends < now) // don't display expired alarms
144 continue;
145
146 cJSON *ej = cJSON_CreateObject();
147 cJSON_AddStringToObject(ej, "err", e->err);
148 cJSON_AddNumberToObject(ej, "set", e->set);
149 cJSON_AddNumberToObject(ej, "start", e->started);
150 cJSON_AddNumberToObject(ej, "end", e->ends);
151 cJSON_AddItemToArray(ea, ej);
152 }
153
154 if ((pe = pthread_mutex_unlock(&status_errs_mt))) {
155 hlog(LOG_ERR, "status_error_json(): could not unlock status_errs_mt: %s", strerror(pe));
156 }
157
158 return ea;
159 }
160
161 /*
162 * status_uname: get operating system name and architecture
163 */
164
165 #define UNAME_LEN 512
status_uname(cJSON * root)166 static void status_uname(cJSON *root)
167 {
168 struct utsname ut;
169 char s[UNAME_LEN];
170
171 if (uname(&ut) < 0) {
172 hlog(LOG_ERR, "status_uname: uname() failed: %s", strerror(errno));
173 return;
174 }
175
176 /* no version info, security by obscurity */
177 snprintf(s, UNAME_LEN, "%s %s", ut.sysname, ut.machine);
178
179 cJSON_AddStringToObject(root, "os", s);
180 }
181
182 /*
183 * Check if motd.html is available
184 */
185
186 #define HTTP_FNAME_LEN 1024
187
status_check_motd(cJSON * node)188 static void status_check_motd(cJSON *node)
189 {
190 char fname[HTTP_FNAME_LEN];
191 struct stat st;
192
193 snprintf(fname, HTTP_FNAME_LEN, "%s/motd.html", webdir);
194 if (stat(fname, &st) == -1)
195 return;
196
197 cJSON_AddStringToObject(node, "motd", "/motd.html");
198 }
199
200 /*
201 * Generate a JSON status string
202 */
203
status_json_string(int no_cache,int periodical)204 char *status_json_string(int no_cache, int periodical)
205 {
206 char *out = NULL;
207 int pe;
208
209 /* if we have a very recent status JSON available, return it instead. */
210 if (!no_cache) {
211 if ((pe = pthread_mutex_lock(&status_json_mt))) {
212 hlog(LOG_ERR, "status_json_string(): could not lock status_json_mt: %s", strerror(pe));
213 return NULL;
214 }
215 if (status_json_cached && (status_json_cache_t == tick || status_json_cache_t == tick - 1)) {
216 out = hstrdup(status_json_cached);
217 if ((pe = pthread_mutex_unlock(&status_json_mt))) {
218 hlog(LOG_ERR, "status_json_string(): could not unlock status_json_mt: %s", strerror(pe));
219 hfree(out);
220 return NULL;
221 }
222 return out;
223 }
224 if ((pe = pthread_mutex_unlock(&status_json_mt))) {
225 hlog(LOG_ERR, "status_json_string(): could not unlock status_json_mt: %s", strerror(pe));
226 return NULL;
227 }
228 }
229
230 /* Ok, go and build the JSON tree */
231 cJSON *root = cJSON_CreateObject();
232 if (http_status_options)
233 cJSON_AddStringToObject(root, "status_options", http_status_options);
234 status_check_motd(root);
235
236 cJSON *server = cJSON_CreateObject();
237 cJSON_AddStringToObject(server, "server_id", serverid);
238 cJSON_AddStringToObject(server, "admin", myadmin);
239 cJSON_AddStringToObject(server, "email", myemail);
240 cJSON_AddStringToObject(server, "software", PROGNAME);
241 cJSON_AddStringToObject(server, "software_version", version_build);
242 cJSON_AddStringToObject(server, "software_build_time", verstr_build_time);
243 cJSON_AddStringToObject(server, "software_build_user", verstr_build_user);
244 cJSON_AddStringToObject(server, "software_build_features", verstr_features);
245 cJSON_AddNumberToObject(server, "uptime", tick - startup_tick);
246 cJSON_AddNumberToObject(server, "tick_now", tick);
247 cJSON_AddNumberToObject(server, "time_now", now);
248 cJSON_AddNumberToObject(server, "time_started", startup_time);
249
250 char q_protocol_id_s[2] = { q_protocol_id, 0 };
251 cJSON_AddStringToObject(server, "q_protocol_id", q_protocol_id_s);
252 cJSON_AddNumberToObject(server, "disallow_other_q_protocols", disallow_other_protocol_id);
253
254 status_uname(server);
255 cJSON_AddItemToObject(root, "server", server);
256
257 cJSON *memory = cJSON_CreateObject();
258 #ifndef _FOR_VALGRIND_
259 struct cellstatus_t cellst;
260 historydb_cell_stats(&cellst),
261 cJSON_AddNumberToObject(memory, "historydb_cells_used", historydb_cellgauge);
262 cJSON_AddNumberToObject(memory, "historydb_cells_free", cellst.freecount);
263 cJSON_AddNumberToObject(memory, "historydb_used_bytes", historydb_cellgauge*cellst.cellsize_aligned);
264 cJSON_AddNumberToObject(memory, "historydb_allocated_bytes", (long)cellst.blocks * (long)cellst.block_size);
265 cJSON_AddNumberToObject(memory, "historydb_block_size", (long)cellst.block_size);
266 cJSON_AddNumberToObject(memory, "historydb_blocks", (long)cellst.blocks);
267 cJSON_AddNumberToObject(memory, "historydb_blocks_max", (long)cellst.blocks_max);
268 cJSON_AddNumberToObject(memory, "historydb_cell_size", cellst.cellsize);
269 cJSON_AddNumberToObject(memory, "historydb_cell_size_aligned", cellst.cellsize_aligned);
270 cJSON_AddNumberToObject(memory, "historydb_cell_align", cellst.alignment);
271
272 dupecheck_cell_stats(&cellst),
273 cJSON_AddNumberToObject(memory, "dupecheck_cells_used", dupecheck_cellgauge);
274 cJSON_AddNumberToObject(memory, "dupecheck_cells_free", cellst.freecount);
275 cJSON_AddNumberToObject(memory, "dupecheck_used_bytes", dupecheck_cellgauge*cellst.cellsize_aligned);
276 cJSON_AddNumberToObject(memory, "dupecheck_allocated_bytes", (long)cellst.blocks * (long)cellst.block_size);
277 cJSON_AddNumberToObject(memory, "dupecheck_block_size", (long)cellst.block_size);
278 cJSON_AddNumberToObject(memory, "dupecheck_blocks", (long)cellst.blocks);
279 cJSON_AddNumberToObject(memory, "dupecheck_blocks_max", (long)cellst.blocks_max);
280 cJSON_AddNumberToObject(memory, "dupecheck_cell_size", cellst.cellsize);
281 cJSON_AddNumberToObject(memory, "dupecheck_cell_size_aligned", cellst.cellsize_aligned);
282 cJSON_AddNumberToObject(memory, "dupecheck_cell_align", cellst.alignment);
283
284 struct cellstatus_t cellst_filter, cellst_filter_wx, cellst_filter_entrycall;
285 filter_cell_stats(&cellst_filter, &cellst_filter_entrycall, &cellst_filter_wx),
286 cJSON_AddNumberToObject(memory, "filter_cells_used", filter_cellgauge);
287 cJSON_AddNumberToObject(memory, "filter_cells_free", cellst_filter.freecount);
288 cJSON_AddNumberToObject(memory, "filter_used_bytes", filter_cellgauge*cellst_filter.cellsize_aligned);
289 cJSON_AddNumberToObject(memory, "filter_allocated_bytes", (long)cellst_filter.blocks * (long)cellst_filter.block_size);
290 cJSON_AddNumberToObject(memory, "filter_block_size", (long)cellst_filter.block_size);
291 cJSON_AddNumberToObject(memory, "filter_blocks", (long)cellst_filter.blocks);
292 cJSON_AddNumberToObject(memory, "filter_blocks_max", (long)cellst_filter.blocks_max);
293 cJSON_AddNumberToObject(memory, "filter_cell_size", cellst_filter.cellsize);
294 cJSON_AddNumberToObject(memory, "filter_cell_size_aligned", cellst_filter.cellsize_aligned);
295 cJSON_AddNumberToObject(memory, "filter_cell_align", cellst_filter.alignment);
296
297 cJSON_AddNumberToObject(memory, "filter_wx_cells_used", filter_wx_cellgauge);
298 cJSON_AddNumberToObject(memory, "filter_wx_cells_free", cellst_filter_wx.freecount);
299 cJSON_AddNumberToObject(memory, "filter_wx_used_bytes", filter_wx_cellgauge*cellst_filter_wx.cellsize_aligned);
300 cJSON_AddNumberToObject(memory, "filter_wx_allocated_bytes", (long)cellst_filter_wx.blocks * (long)cellst_filter_wx.block_size);
301 cJSON_AddNumberToObject(memory, "filter_wx_block_size", (long)cellst_filter_wx.block_size);
302 cJSON_AddNumberToObject(memory, "filter_wx_blocks", (long)cellst_filter_wx.blocks);
303 cJSON_AddNumberToObject(memory, "filter_wx_blocks_max", (long)cellst_filter_wx.blocks_max);
304 cJSON_AddNumberToObject(memory, "filter_wx_cell_size", cellst_filter_wx.cellsize);
305 cJSON_AddNumberToObject(memory, "filter_wx_cell_size_aligned", cellst_filter_wx.cellsize_aligned);
306 cJSON_AddNumberToObject(memory, "filter_wx_cell_align", cellst_filter_wx.alignment);
307
308 cJSON_AddNumberToObject(memory, "filter_entrycall_cells_used", filter_entrycall_cellgauge);
309 cJSON_AddNumberToObject(memory, "filter_entrycall_cells_free", cellst_filter_entrycall.freecount);
310 cJSON_AddNumberToObject(memory, "filter_entrycall_used_bytes", filter_entrycall_cellgauge*cellst_filter_entrycall.cellsize_aligned);
311 cJSON_AddNumberToObject(memory, "filter_entrycall_allocated_bytes", (long)cellst_filter_entrycall.blocks * (long)cellst_filter_entrycall.block_size);
312 cJSON_AddNumberToObject(memory, "filter_entrycall_block_size", (long)cellst_filter_entrycall.block_size);
313 cJSON_AddNumberToObject(memory, "filter_entrycall_blocks", (long)cellst_filter_entrycall.blocks);
314 cJSON_AddNumberToObject(memory, "filter_entrycall_blocks_max", (long)cellst_filter_entrycall.blocks_max);
315 cJSON_AddNumberToObject(memory, "filter_entrycall_cell_size", cellst_filter_entrycall.cellsize);
316 cJSON_AddNumberToObject(memory, "filter_entrycall_cell_size_aligned", cellst_filter_entrycall.cellsize_aligned);
317 cJSON_AddNumberToObject(memory, "filter_entrycall_cell_align", cellst_filter_entrycall.alignment);
318
319 struct cellstatus_t cellst_pbuf_small, cellst_pbuf_medium, cellst_pbuf_large;
320 incoming_cell_stats(&cellst_pbuf_small, &cellst_pbuf_medium, &cellst_pbuf_large);
321 cJSON_AddNumberToObject(memory, "pbuf_small_cells_used", cellst_pbuf_small.cellcount - cellst_pbuf_small.freecount);
322 cJSON_AddNumberToObject(memory, "pbuf_small_cells_free", cellst_pbuf_small.freecount);
323 cJSON_AddNumberToObject(memory, "pbuf_small_cells_alloc", cellst_pbuf_small.cellcount);
324 cJSON_AddNumberToObject(memory, "pbuf_small_used_bytes", (cellst_pbuf_small.cellcount - cellst_pbuf_small.freecount)*cellst_pbuf_small.cellsize_aligned);
325 cJSON_AddNumberToObject(memory, "pbuf_small_allocated_bytes", (long)cellst_pbuf_small.blocks * (long)cellst_pbuf_small.block_size);
326 cJSON_AddNumberToObject(memory, "pbuf_small_block_size", (long)cellst_pbuf_small.block_size);
327 cJSON_AddNumberToObject(memory, "pbuf_small_blocks", (long)cellst_pbuf_small.blocks);
328 cJSON_AddNumberToObject(memory, "pbuf_small_blocks_max", (long)cellst_pbuf_small.blocks_max);
329 cJSON_AddNumberToObject(memory, "pbuf_small_cell_size", cellst_pbuf_small.cellsize);
330 cJSON_AddNumberToObject(memory, "pbuf_small_cell_size_aligned", cellst_pbuf_small.cellsize_aligned);
331 cJSON_AddNumberToObject(memory, "pbuf_small_cell_align", cellst_pbuf_small.alignment);
332
333 cJSON_AddNumberToObject(memory, "pbuf_medium_cells_used", cellst_pbuf_medium.cellcount - cellst_pbuf_medium.freecount);
334 cJSON_AddNumberToObject(memory, "pbuf_medium_cells_free", cellst_pbuf_medium.freecount);
335 cJSON_AddNumberToObject(memory, "pbuf_medium_cells_alloc", cellst_pbuf_medium.cellcount);
336 cJSON_AddNumberToObject(memory, "pbuf_medium_used_bytes", (cellst_pbuf_medium.cellcount - cellst_pbuf_medium.freecount)*cellst_pbuf_medium.cellsize_aligned);
337 cJSON_AddNumberToObject(memory, "pbuf_medium_allocated_bytes", (long)cellst_pbuf_medium.blocks * (long)cellst_pbuf_medium.block_size);
338 cJSON_AddNumberToObject(memory, "pbuf_medium_block_size", (long)cellst_pbuf_medium.block_size);
339 cJSON_AddNumberToObject(memory, "pbuf_medium_blocks", (long)cellst_pbuf_medium.blocks);
340 cJSON_AddNumberToObject(memory, "pbuf_medium_blocks_max", (long)cellst_pbuf_medium.blocks_max);
341 cJSON_AddNumberToObject(memory, "pbuf_medium_cell_size", cellst_pbuf_medium.cellsize);
342 cJSON_AddNumberToObject(memory, "pbuf_medium_cell_size_aligned", cellst_pbuf_medium.cellsize_aligned);
343 cJSON_AddNumberToObject(memory, "pbuf_medium_cell_align", cellst_pbuf_medium.alignment);
344
345 cJSON_AddNumberToObject(memory, "pbuf_large_cells_used", cellst_pbuf_large.cellcount - cellst_pbuf_large.freecount);
346 cJSON_AddNumberToObject(memory, "pbuf_large_cells_free", cellst_pbuf_large.freecount);
347 cJSON_AddNumberToObject(memory, "pbuf_large_cells_alloc", cellst_pbuf_large.cellcount);
348 cJSON_AddNumberToObject(memory, "pbuf_large_used_bytes", (cellst_pbuf_large.cellcount - cellst_pbuf_large.freecount)*cellst_pbuf_large.cellsize_aligned);
349 cJSON_AddNumberToObject(memory, "pbuf_large_allocated_bytes", (long)cellst_pbuf_large.blocks * (long)cellst_pbuf_large.block_size);
350 cJSON_AddNumberToObject(memory, "pbuf_large_block_size", (long)cellst_pbuf_large.block_size);
351 cJSON_AddNumberToObject(memory, "pbuf_large_blocks", (long)cellst_pbuf_large.blocks);
352 cJSON_AddNumberToObject(memory, "pbuf_large_blocks_max", (long)cellst_pbuf_large.blocks_max);
353 cJSON_AddNumberToObject(memory, "pbuf_large_cell_size", cellst_pbuf_large.cellsize);
354 cJSON_AddNumberToObject(memory, "pbuf_large_cell_size_aligned", cellst_pbuf_large.cellsize_aligned);
355 cJSON_AddNumberToObject(memory, "pbuf_large_cell_align", cellst_pbuf_large.alignment);
356
357 struct cellstatus_t cellst_client_heard;
358 client_heard_cell_stats(&cellst_client_heard);
359 cJSON_AddNumberToObject(memory, "client_heard_cells_used", cellst_client_heard.cellcount - cellst_client_heard.freecount);
360 cJSON_AddNumberToObject(memory, "client_heard_cells_free", cellst_client_heard.freecount);
361 cJSON_AddNumberToObject(memory, "client_heard_cells_alloc", cellst_client_heard.cellcount);
362 cJSON_AddNumberToObject(memory, "client_heard_used_bytes", (cellst_client_heard.cellcount - cellst_client_heard.freecount)*cellst_client_heard.cellsize_aligned);
363 cJSON_AddNumberToObject(memory, "client_heard_allocated_bytes", (long)cellst_client_heard.blocks * (long)cellst_client_heard.block_size);
364 cJSON_AddNumberToObject(memory, "client_heard_block_size", (long)cellst_client_heard.block_size);
365 cJSON_AddNumberToObject(memory, "client_heard_blocks", (long)cellst_client_heard.blocks);
366 cJSON_AddNumberToObject(memory, "client_heard_blocks_max", (long)cellst_client_heard.blocks_max);
367 cJSON_AddNumberToObject(memory, "client_heard_cell_size", cellst_client_heard.cellsize);
368 cJSON_AddNumberToObject(memory, "client_heard_cell_size_aligned", cellst_client_heard.cellsize_aligned);
369 cJSON_AddNumberToObject(memory, "client_heard_cell_align", cellst_client_heard.alignment);
370 #endif
371
372 cJSON_AddItemToObject(root, "memory", memory);
373
374 cJSON *historydb = cJSON_CreateObject();
375 cJSON_AddNumberToObject(historydb, "inserts", historydb_inserts);
376 cJSON_AddNumberToObject(historydb, "lookups", historydb_lookups);
377 cJSON_AddNumberToObject(historydb, "hashmatches", historydb_hashmatches);
378 cJSON_AddNumberToObject(historydb, "keymatches", historydb_keymatches);
379 cJSON_AddNumberToObject(historydb, "noposcount", historydb_noposcount);
380 cJSON_AddNumberToObject(historydb, "cleaned", historydb_cleanup_cleaned);
381 cJSON_AddItemToObject(root, "historydb", historydb);
382
383 cJSON *dupecheck = cJSON_CreateObject();
384 cJSON_AddNumberToObject(dupecheck, "dupes_dropped", dupecheck_dupecount);
385 cJSON_AddNumberToObject(dupecheck, "uniques_out", dupecheck_outcount);
386 cJSON_AddItemToObject(root, "dupecheck", dupecheck);
387
388 cJSON *dupe_vars = cJSON_CreateObject();
389 cJSON_AddNumberToObject(dupe_vars, "exact", dupecheck_dupetypes[0]);
390 cJSON_AddNumberToObject(dupe_vars, "space_trim", dupecheck_dupetypes[DTYPE_SPACE_TRIM]);
391 cJSON_AddNumberToObject(dupe_vars, "8bit_strip", dupecheck_dupetypes[DTYPE_STRIP_8BIT]);
392 cJSON_AddNumberToObject(dupe_vars, "8bit_clear", dupecheck_dupetypes[DTYPE_CLEAR_8BIT]);
393 cJSON_AddNumberToObject(dupe_vars, "8bit_spaced", dupecheck_dupetypes[DTYPE_SPACED_8BIT]);
394 cJSON_AddNumberToObject(dupe_vars, "low_strip", dupecheck_dupetypes[DTYPE_LOWDATA_STRIP]);
395 cJSON_AddNumberToObject(dupe_vars, "low_spaced", dupecheck_dupetypes[DTYPE_LOWDATA_SPACED]);
396 cJSON_AddNumberToObject(dupe_vars, "del_strip", dupecheck_dupetypes[DTYPE_DEL_STRIP]);
397 cJSON_AddNumberToObject(dupe_vars, "del_spaced", dupecheck_dupetypes[DTYPE_DEL_SPACED]);
398 cJSON_AddItemToObject(dupecheck, "variations", dupe_vars);
399
400 cJSON *json_totals = cJSON_CreateObject();
401 cJSON *json_listeners = cJSON_CreateArray();
402 accept_listener_status(json_listeners, json_totals);
403 cJSON_AddItemToObject(root, "totals", json_totals);
404 cJSON_AddItemToObject(root, "listeners", json_listeners);
405
406 cJSON *json_clients = cJSON_CreateArray();
407 cJSON *json_uplinks = cJSON_CreateArray();
408 cJSON *json_peers = cJSON_CreateArray();
409 cJSON *json_workers = cJSON_CreateArray();
410 worker_client_list(json_workers, json_clients, json_uplinks, json_peers, json_totals, memory);
411 cJSON_AddItemToObject(root, "workers", json_workers);
412 cJSON_AddItemToObject(root, "uplinks", json_uplinks);
413 cJSON_AddItemToObject(root, "peers", json_peers);
414 cJSON_AddItemToObject(root, "clients", json_clients);
415
416 /* if this is a periodical per-minute dump, collect historical data */
417 if (periodical) {
418 cJSON *ct, *cv;
419 struct cdata_list_t *cl;
420 for (cl = cdata_list; (cl); cl = cl->next) {
421 ct = cJSON_GetObjectItem(root, cl->tree);
422 if (!ct)
423 continue;
424
425 cv = cJSON_GetObjectItem(ct, cl->name);
426
427 /* cJSON's cv->valueint is just an integer, which will overflow
428 * too quickly. So, let's take the more expensive valuedouble.
429 */
430 if (cl->gauge)
431 cdata_gauge_sample(cl->cd, (cv) ? cv->valuedouble : -1);
432 else
433 cdata_counter_sample(cl->cd, (cv) ? cv->valuedouble : -1);
434 }
435 }
436
437 cJSON_AddNumberToObject(json_totals, "tcp_bytes_rx_rate", cdata_get_last_value("totals.tcp_bytes_rx") / CDATA_INTERVAL);
438 cJSON_AddNumberToObject(json_totals, "tcp_bytes_tx_rate", cdata_get_last_value("totals.tcp_bytes_tx") / CDATA_INTERVAL);
439 cJSON_AddNumberToObject(json_totals, "udp_bytes_rx_rate", cdata_get_last_value("totals.udp_bytes_rx") / CDATA_INTERVAL);
440 cJSON_AddNumberToObject(json_totals, "udp_bytes_tx_rate", cdata_get_last_value("totals.udp_bytes_tx") / CDATA_INTERVAL);
441 cJSON_AddNumberToObject(json_totals, "bytes_rx_rate", (cdata_get_last_value("totals.tcp_bytes_rx") + cdata_get_last_value("totals.udp_bytes_rx")) / CDATA_INTERVAL);
442 cJSON_AddNumberToObject(json_totals, "bytes_tx_rate", (cdata_get_last_value("totals.tcp_bytes_tx") + cdata_get_last_value("totals.udp_bytes_tx")) / CDATA_INTERVAL);
443
444 cJSON *json_rx_errs = cJSON_CreateStringArray(inerr_labels, INERR_BUCKETS);
445 cJSON_AddItemToObject(root, "rx_errs", json_rx_errs);
446
447 cJSON_AddItemToObject(root, "alarms", status_error_json());
448
449 /* the tree is built, print it out to a malloc'ed string */
450 out = cJSON_Print(root);
451 cJSON_Delete(root);
452
453 /* cache it */
454 if ((pe = pthread_mutex_lock(&status_json_mt))) {
455 hlog(LOG_ERR, "status_json_string(): could not lock status_json_mt: %s", strerror(pe));
456 return NULL;
457 }
458 if (status_json_cached)
459 hfree(status_json_cached);
460
461 status_json_cached = hstrdup(out);
462 status_json_cache_t = tick;
463
464 if ((pe = pthread_mutex_unlock(&status_json_mt))) {
465 hlog(LOG_ERR, "status_json_string(): could not unlock status_json_mt: %s", strerror(pe));
466 return NULL;
467 }
468
469 return out;
470 }
471
472 #define PATHLEN 500
473
json_write_file(char * basename,const char * s)474 int json_write_file(char *basename, const char *s)
475 {
476 char path[PATHLEN+1];
477 char tmppath[PATHLEN+1];
478 FILE *fp;
479 time_t start_t, end_t;
480
481 time(&start_t);
482
483 if (snprintf(path, PATHLEN, "%s/%s.json", rundir, basename) >= PATHLEN-1
484 || snprintf(tmppath, PATHLEN, "%s.tmp", path) >= PATHLEN-1) {
485 hlog(LOG_ERR, "json file write failed: Too long path");
486 return -1;
487 }
488
489 fp = fopen(tmppath,"w");
490 if (!fp) {
491 hlog(LOG_ERR, "json file write failed: Could not open %s for writing: %s", tmppath, strerror(errno));
492 return -1;
493 }
494
495 if (fputs(s, fp) == EOF) {
496 hlog(LOG_ERR, "json file write failed: Could not write to %s: %s", tmppath, strerror(errno));
497 fclose(fp);
498 return -1;
499 }
500
501 if (fclose(fp)) {
502 hlog(LOG_ERR, "json file update failed: close(%s): %s", tmppath, strerror(errno));
503 return -1;
504 }
505
506 if (rename(tmppath, path)) {
507 hlog(LOG_ERR, "json file update failed: Could not rename %s to %s: %s", tmppath, path, strerror(errno));
508 return -1;
509 }
510
511 /* check if we're having I/O delays */
512 time(&end_t);
513 if (end_t - start_t > 2) {
514 hlog(LOG_ERR, "json file update took %d seconds", end_t - start_t);
515 }
516
517 return 0;
518 }
519
520 /*
521 * Status dumping to file is currently disabled, since doing any
522 * significant I/O seems to have the risk of blocking the whole process
523 * when the server is running under VMWare.
524 */
525
526 #ifdef ENABLE_STATUS_DUMP_FILE
527
status_dump_fp(FILE * fp)528 static int status_dump_fp(FILE *fp)
529 {
530 char *out = status_json_string(1, 1);
531 fputs(out, fp);
532 hfree(out);
533
534 return 0;
535 }
536
status_dump_file(void)537 int status_dump_file(void)
538 {
539 char path[PATHLEN+1];
540 char tmppath[PATHLEN+1];
541 FILE *fp;
542 time_t start_t, end_t;
543
544 time(&start_t);
545
546 snprintf(path, PATHLEN, "%s/aprsc-status.json", rundir);
547 snprintf(tmppath, PATHLEN, "%s.tmp", path);
548 fp = fopen(tmppath,"w");
549 if (!fp) {
550 hlog(LOG_ERR, "status file update failed: Could not open %s for writing: %s", tmppath, strerror(errno));
551 return -1;
552 }
553
554 status_dump_fp(fp);
555
556 if (fclose(fp)) {
557 hlog(LOG_ERR, "status file update failed: close(%s): %s", tmppath, strerror(errno));
558 return -1;
559 }
560
561 if (rename(tmppath, path)) {
562 hlog(LOG_ERR, "status file update failed: Could not rename %s to %s: %s", tmppath, path, strerror(errno));
563 return -1;
564 }
565
566 /* check if we're having I/O delays */
567 time(&end_t);
568 if (end_t - start_t > 2) {
569 hlog(LOG_ERR, "status file update took %d seconds", end_t - start_t);
570 }
571
572 return 0;
573 }
574
575 #else
576
577 /*
578 * The code to update counterdata is embedded in the status JSON
579 * generator. If dump-status-to-file-per-minute is disabled,
580 * do generate the JSON just to update the counters for graphs.
581 * TODO: just update the cdata counters without generating the JSON.
582 */
583
status_dump_file(void)584 int status_dump_file(void)
585 {
586 time_t start_t, end_t;
587
588 time(&start_t);
589
590 char *out = status_json_string(1, 1);
591 hfree(out);
592
593 /* check if we're having delays */
594 time(&end_t);
595 if (end_t - start_t > 2) {
596 hlog(LOG_ERR, "status counters update took %d seconds", end_t - start_t);
597 }
598
599 return 0;
600 }
601
602 #endif
603
604 /*
605 * Save enough status to a JSON file so that live upgrade can continue
606 * serving existing clients with it
607 */
608
status_dump_liveupgrade(void)609 int status_dump_liveupgrade(void)
610 {
611 const char *out;
612
613 if (!worker_shutdown_clients) {
614 return 0;
615 }
616
617 cJSON *root = cJSON_CreateObject();
618 cJSON_AddItemToObject(root, "clients", worker_shutdown_clients);
619
620 /* rx error counter labels */
621 cJSON *json_rx_errs = cJSON_CreateStringArray(inerr_labels, INERR_BUCKETS);
622 cJSON_AddItemToObject(root, "rx_errs", json_rx_errs);
623
624 out = cJSON_Print(root);
625 cJSON_Delete(root);
626 worker_shutdown_clients = NULL;
627
628 return json_write_file("liveupgrade", out);
629 }
630
status_read_liveupgrade(void)631 int status_read_liveupgrade(void)
632 {
633 char path[PATHLEN+1];
634 char path_renamed[PATHLEN+1];
635 FILE *fp;
636 char *s = NULL;
637 int sl = 0;
638 char buf[32768];
639 int i;
640
641 snprintf(path, PATHLEN, "%s/liveupgrade.json", rundir);
642 snprintf(path_renamed, PATHLEN, "%s/liveupgrade.json.old", rundir);
643
644 fp = fopen(path, "r");
645 if (!fp) {
646 hlog(LOG_ERR, "liveupgrade dump file read failed: Could not open %s for reading: %s", path, strerror(errno));
647 return -1;
648 }
649
650 hlog(LOG_INFO, "Live upgrade: Loading client status from %s ...", path);
651
652 if (rename(path, path_renamed) < 0) {
653 hlog(LOG_ERR, "Failed to rename liveupgrade dump file %s to %s: %s",
654 path, path_renamed, strerror(errno));
655 unlink(path);
656 }
657
658 while ((i = fread(buf, 1, sizeof(buf), fp)) > 0) {
659 //hlog(LOG_DEBUG, "read %d bytes", i);
660 s = hrealloc(s, sl + i+1);
661 memcpy(s + sl, buf, i);
662 sl += i;
663 s[sl] = 0; // keep null-terminated
664 //hlog(LOG_DEBUG, "now: %s", s);
665 }
666
667 if (fclose(fp)) {
668 hlog(LOG_ERR, "liveupgrade dump file read failed: close(%s): %s", path, strerror(errno));
669 hfree(s);
670 return -1;
671 }
672
673 /* decode JSON */
674 // coverity[tainted_data] // squelch warning: the json file is read from disk, written by ourself
675 cJSON *dec = cJSON_Parse(s);
676 if (!dec) {
677 hlog(LOG_ERR, "liveupgrade dump parsing failed");
678 hfree(s);
679 return -1;
680 }
681
682 liveupgrade_status = dec;
683 hfree(s);
684
685 return 0;
686 }
687
status_init(void)688 void status_init(void)
689 {
690 int i;
691 char *n;
692
693 static const char *cdata_start[][3] = {
694 { "totals", "clients", "g" },
695 { "totals", "connects", "c" },
696 { "totals", "tcp_bytes_rx", "c" },
697 { "totals", "tcp_bytes_tx", "c" },
698 { "totals", "udp_bytes_rx", "c" },
699 { "totals", "udp_bytes_tx", "c" },
700 { "totals", "tcp_pkts_rx", "c" },
701 { "totals", "tcp_pkts_tx", "c" },
702 { "totals", "udp_pkts_rx", "c" },
703 { "totals", "udp_pkts_tx", "c" },
704 #ifdef USE_SCTP
705 { "totals", "sctp_bytes_rx", "c" },
706 { "totals", "sctp_bytes_tx", "c" },
707 { "totals", "sctp_pkts_rx", "c" },
708 { "totals", "sctp_pkts_tx", "c" },
709 #endif
710 { "dupecheck", "dupes_dropped", "c" },
711 { "dupecheck", "uniques_out", "c" },
712 { NULL, NULL }
713 };
714
715 i = 0;
716 while (cdata_start[i][0] != NULL) {
717 n = hmalloc(strlen(cdata_start[i][0]) + 1 + strlen(cdata_start[i][1]) + 1);
718 sprintf(n, "%s.%s", cdata_start[i][0], cdata_start[i][1]);
719 struct cdata_list_t *cl = hmalloc(sizeof(*cl));
720 cl->tree = cdata_start[i][0];
721 cl->name = cdata_start[i][1];
722 cl->next = cdata_list;
723 cl->cd = cdata_alloc(n);
724 hfree(n);
725 cl->gauge = cdata_start[i][2][0] == 'g' ? 1 : 0;
726 cdata_list = cl;
727 i++;
728 }
729 }
730
status_atend(void)731 void status_atend(void)
732 {
733 struct cdata_list_t *cl, *cl_next;
734 int pe;
735
736 for (cl = cdata_list; (cl); cl = cl_next) {
737 cl_next = cl->next;
738 cdata_free(cl->cd);
739 hfree(cl);
740 }
741
742 if ((pe = pthread_mutex_lock(&status_json_mt))) {
743 hlog(LOG_ERR, "status_atend(): could not lock status_json_mt: %s", strerror(pe));
744 return;
745 }
746
747 if (status_json_cached) {
748 hfree(status_json_cached);
749 status_json_cached = NULL;
750 }
751
752 if ((pe = pthread_mutex_unlock(&status_json_mt))) {
753 hlog(LOG_ERR, "status_atend(): could not unlock status_json_mt: %s", strerror(pe));
754 return;
755 }
756 }
757
758 /*
759 * Helper functions for encoding binary data to string buffers in JSON
760 */
761
hex_encode(const char * buf,int len)762 char *hex_encode(const char *buf, int len)
763 {
764 static const char *lut = "0123456789abcdef";
765 int i;
766 char *s = hmalloc(len*2+1);
767
768 for (i = 0; i < len; i++) {
769 const char c = buf[i];
770 s[i*2] = lut[c >> 4];
771 s[i*2+1] = lut[c & 15];
772 }
773 s[i*2] = 0;
774
775 return s;
776 }
777
hex_decode(char * obuf,int olen,const char * hex)778 int hex_decode(char *obuf, int olen, const char *hex)
779 {
780 int i;
781 int len = strlen(hex);
782 int oi = 0;
783
784 if (len & 1)
785 return -1; // odd length
786
787 if (olen < len / 2)
788 return -1; // output buffer too large
789
790 for (i = 0; i < len; i += 2) {
791 char h = toupper(hex[i]);
792 if (h >= 0x30 && h <= 0x39)
793 h -= 0x30; /* 0..9 are now right */
794 else if (h >= 0x41 && h <= 0x46)
795 h = h - 0x41 + 10; /* A-F are now right */
796 else
797 return -1;
798
799 char l = toupper(hex[i+1]);
800 if (l >= 0x30 && l <= 0x39)
801 l -= 0x30; /* 0..9 are now right */
802 else if (l >= 0x41 && l <= 0x46)
803 l = l - 0x41 + 10; /* A-F are now right */
804 else
805 return -1;
806
807 obuf[oi++] = h*16 + l;
808 }
809
810 return oi;
811 }
812