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