summaryrefslogtreecommitdiffstats
path: root/modules/luci-mod-dashboard/root/usr/share/rpcd/ucode/luci.dashboard
blob: d87679d9b0273fdf2f682e7e58092a29f8d3cc90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
'use strict';

import { readfile } from 'fs';
import * as uloop from 'uloop';

const STEP = 5000, SLOTS = 60;

let ring = [], seq = 0, timer;

function now() {
	const c = clock(true);

	return c[0] * 1000 + int(c[1] / 1000000);
}

// Tells the instances apart, as the sequence starts over with rpcd.
const id = trim(readfile('/proc/sys/kernel/random/uuid'));

// Counters as the kernel has them. Whoever draws them takes the differences,
// over the time between two samples on the clock they were read by.
function sample() {
	const t = now(), cpus = {}, net = {};

	for (let line in split(readfile('/proc/stat') ?? '', '\n')) {
		if (substr(line, 0, 3) != 'cpu')
			break;

		const fields = split(line, /\s+/);

		cpus[fields[0]] = map(slice(fields, 1, 9), v => +v);
	}

	const mem = match(readfile('/proc/meminfo', 256) ?? '', /MemAvailable:\s+(\d+)/);

	for (let line in slice(split(readfile('/proc/net/dev') ?? '', '\n'), 2)) {
		const fields = split(trim(line), /[: ]+/);

		if (length(fields) > 9 && fields[0] != 'lo')
			net[fields[0]] = [ +fields[1], +fields[9] ];
	}

	push(ring, [ ++seq, t, cpus, mem ? +mem[1] * 1024 : null, net ]);

	while (length(ring) > SLOTS)
		shift(ring);
}

// Sampling never stops, so the charts are full whenever somebody looks.
function tick() {
	timer.set(STEP);

	// An exception here would end rpcd's uloop, not just this timer.
	try {
		sample();
	}
	catch (e) {
		warn(`luci.dashboard: ${e}\n`);
	}
}

timer = uloop.timer(0, tick);

const methods = {
	history: {
		args: { since: 0, id: 'id' },
		call: function(request) {
			const since = (request.args?.id == id) ? +(request.args?.since ?? 0) : 0;

			return {
				id,
				now: now(),
				step: STEP,
				slots: SLOTS,
				samples: filter(ring, entry => entry[0] > since)
			};
		}
	}
};

return { 'luci.dashboard': methods };