Skip to content

Commit fd3881a

Browse files
Add events to track clients and servers
1 parent dfae1a2 commit fd3881a

File tree

5 files changed

+212
-0
lines changed

5 files changed

+212
-0
lines changed

hc_http.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,12 @@
4646
#include "echttp_cors.h"
4747
#include "echttp_static.h"
4848
#include "houseportalclient.h"
49+
#include "houselog.h"
4950

5051
static pid_t parent;
5152

53+
static long hc_known_clients[256]; // Enough to store IP v4 address.
54+
static long hc_known_servers[256]; // Enough to store IP v4 address.
5255

5356
static hc_clock_status *clock_db = 0;
5457
static hc_nmea_status *nmea_db = 0;
@@ -63,6 +66,7 @@ static char JsonBuffer[16384];
6366
static void hc_background (int fd, int mode) {
6467
static time_t LastParentCheck = 0;
6568
static time_t LastRenewal = 0;
69+
static time_t LastActivityCheck = 0;
6670

6771
time_t now = time(0);
6872

@@ -90,6 +94,89 @@ static void hc_background (int fd, int mode) {
9094
LastRenewal = now;
9195
}
9296
}
97+
98+
if (ntp_db && (now >= LastActivityCheck + 5)) {
99+
100+
101+
// Generate events for new or unsynchronized clients.
102+
// We generate a local "cache" of known clients to limit the number of
103+
// events generated when the clent is not synchronized. The cache key
104+
// is the low 7 bits of the IP address, plus the ninth bit: this works
105+
// best for me because I have two subnets, while I don't have anywhere
106+
// close to 127 machines at home.
107+
// This should work fine for most home networks.
108+
//
109+
int i;
110+
for (i = 0; i < HC_NTP_DEPTH; ++i) {
111+
struct hc_ntp_client *client = ntp_db->clients + i;
112+
113+
// Do not consider events that are empty or too old (risk of
114+
// race condition)
115+
//
116+
if ((client->local.tv_sec < LastActivityCheck)
117+
|| (client->local.tv_sec == 0)) continue;
118+
119+
// Do not consider events that were already detected.
120+
//
121+
if (client->logged) continue;
122+
123+
if (abs(client->origin.tv_sec - client->local.tv_sec) > 600) {
124+
houselog_event ("CLIENT", hc_broadcast_format (&(client->address)),
125+
"ACTIVE", "NOT SYNCHRONIZED");
126+
} else {
127+
long adr = ntohl(client->address.sin_addr.s_addr);
128+
int hash = (int) ((adr & 0x7f) | ((adr & 0x100) >> 1));
129+
130+
int delta =
131+
((client->origin.tv_sec - client->local.tv_sec) * 1000)
132+
+ ((client->origin.tv_usec - client->local.tv_usec) / 1000);
133+
134+
if ((hc_known_clients[hash] == adr) && (abs(delta) < 10000)) continue;
135+
houselog_event ("CLIENT", hc_broadcast_format (&(client->address)),
136+
"ACTIVE", "DELTA %d MS", delta);
137+
138+
hc_known_clients[hash] = adr;
139+
}
140+
client->logged = 1;
141+
}
142+
143+
// Generate events for newly detected servers, using a similar cache
144+
// as for clients to limit the rate of events when synchronized.
145+
//
146+
for (i = 0; i < HC_NTP_POOL; ++i) {
147+
struct hc_ntp_server *server = ntp_db->pool + i;
148+
149+
// Do not consider events that are empty or too old (risk of
150+
// race condition)
151+
//
152+
if ((server->local.tv_sec < LastActivityCheck)
153+
|| (server->local.tv_sec == 0)) continue;
154+
155+
// Do not consider events that were already detected.
156+
//
157+
if (server->logged) continue;
158+
159+
if (abs(server->origin.tv_sec - server->local.tv_sec) > 600) {
160+
houselog_event ("SERVER", server->name, "ACTIVE",
161+
"STRATUM %d, NOT SYNCHRONIZED", server->stratum);
162+
} else {
163+
long adr = ntohl(server->address.sin_addr.s_addr);
164+
int hash = (int) ((adr & 0x7f) | ((adr & 0x100) >> 1));
165+
166+
int delta =
167+
((server->origin.tv_sec - server->local.tv_sec) * 1000)
168+
+ ((server->origin.tv_usec - server->local.tv_usec) / 1000);
169+
170+
if ((hc_known_servers[hash] == adr) && (abs(delta) < 10000)) continue;
171+
houselog_event ("SERVER", server->name, "ACTIVE",
172+
"STRATUM %d, DELTA %d MS", server->stratum, delta);
173+
174+
hc_known_servers[hash] = adr;
175+
}
176+
server->logged = 1;
177+
}
178+
LastActivityCheck = now;
179+
}
93180
}
94181

95182
static void *hc_http_attach (const char *name) {
@@ -474,6 +561,7 @@ void hc_http (int argc, const char **argv) {
474561
houseportal_initialize (argc, argv);
475562
use_houseportal = 1;
476563
}
564+
houselog_initialize ("ntp", argc, argv);
477565

478566
echttp_cors_allow_method("GET");
479567
echttp_protect (0, hc_protect);
@@ -485,6 +573,7 @@ void hc_http (int argc, const char **argv) {
485573
echttp_route_uri ("/ntp/server", hc_http_ntp);
486574
echttp_static_route ("/", "/usr/local/share/house/public");
487575
echttp_background (&hc_background);
576+
houselog_event ("SERVICE", "ntp", "STARTED", "ON %s", houselog_host());
488577
echttp_loop();
489578
exit (0);
490579
}

hc_ntp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ static void hc_ntp_broadcastmsg (const ntpHeaderV3 *head,
305305
hc_ntp_status_db->pool[sender].stratum = head->stratum;
306306
hc_ntp_get_timestamp
307307
(&(hc_ntp_status_db->pool[sender].origin), &(head->transmit));
308+
hc_ntp_status_db->pool[sender].logged = 0;
308309

309310
// Elect a time source. Choose the lowest stratum available.
310311
//
@@ -413,6 +414,7 @@ static void hc_ntp_requestmsg (const ntpHeaderV3 *head,
413414
(&(hc_ntp_status_db->clients[hc_ntp_client_cursor].origin),
414415
&(ntpResponse.origin));
415416
hc_ntp_status_db->clients[hc_ntp_client_cursor].local = *receive;
417+
hc_ntp_status_db->clients[hc_ntp_client_cursor].logged = 0;
416418
}
417419

418420

hc_ntp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct hc_ntp_client {
4444
struct sockaddr_in address;
4545
struct timeval origin;
4646
struct timeval local;
47+
int logged;
4748
};
4849

4950
struct hc_ntp_server {
@@ -52,6 +53,7 @@ struct hc_ntp_server {
5253
short stratum;
5354
struct sockaddr_in address;
5455
char name[48];
56+
int logged;
5557
};
5658

5759
typedef struct {

public/events.html

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<link rel=stylesheet type="text/css" href="/house.css" title="House">
5+
<script>
6+
function ntpShowStatus (response) {
7+
document.getElementById('portal').href = 'http://'+response.proxy+'/index.html';
8+
}
9+
10+
function ntpStatus () {
11+
var command = new XMLHttpRequest();
12+
command.open("GET", "/ntp/status");
13+
command.onreadystatechange = function () {
14+
if (command.readyState === 4 && command.status === 200) {
15+
ntpShowStatus (JSON.parse(command.responseText));
16+
}
17+
}
18+
command.send(null);
19+
}
20+
window.onload = function() {
21+
22+
function newColumn (text) {
23+
var column = document.createElement("td");
24+
column.innerHTML = text;
25+
return column;
26+
}
27+
28+
var lastEventId = null;
29+
30+
function showEvents (response) {
31+
32+
if (!lastEventId) {
33+
var title = response.host + ' - Kasa Devices';
34+
document.getElementsByTagName ('title')[0].innerHTML = title;
35+
var elements = document.getElementsByClassName ('hostname');
36+
for (var i = 0; i < elements.length; i++) {
37+
elements[i].innerHTML = response.host;
38+
}
39+
}
40+
41+
lastEventId = response.ntp.latest;
42+
43+
var table = document.getElementsByClassName ('eventlist')[0];
44+
for (var i = table.childNodes.length - 1; i > 1; i--) {
45+
table.removeChild(table.childNodes[i]);
46+
}
47+
for (var i = response.ntp.events.length-1; i >= 0; --i) {
48+
var event = response.ntp.events[i];
49+
var timestamp = new Date(event[0]);
50+
var row = document.createElement("tr");
51+
row.appendChild(newColumn(timestamp.toLocaleString()));
52+
row.appendChild(newColumn(event[1]));
53+
row.appendChild(newColumn(event[2]));
54+
row.appendChild(newColumn(event[3]));
55+
row.appendChild(newColumn(event[4]));
56+
table.appendChild(row);
57+
}
58+
}
59+
60+
function updateEvents() {
61+
62+
var command = new XMLHttpRequest();
63+
command.open("GET", "/ntp/log/events");
64+
command.onreadystatechange = function () {
65+
if (command.readyState === 4 && command.status === 200) {
66+
showEvents (JSON.parse(command.responseText));
67+
}
68+
}
69+
command.send(null);
70+
}
71+
72+
function checkEvents () {
73+
74+
var command = new XMLHttpRequest();
75+
command.open("GET", "/ntp/log/latest");
76+
command.onreadystatechange = function () {
77+
if (command.readyState === 4 && command.status === 200) {
78+
var response = JSON.parse(command.responseText);
79+
if ((lastEventId == null) ||
80+
(response.ntp.latest != lastEventId)) updateEvents ();
81+
}
82+
}
83+
command.send(null);
84+
}
85+
86+
updateEvents();
87+
setInterval (function() {checkEvents()}, 1000);
88+
ntpStatus();
89+
}
90+
</script>
91+
<head>
92+
<title></title>
93+
</head>
94+
<body>
95+
<table class="housetopcontainer">
96+
<tr><td>
97+
<table class="housetop">
98+
<tr>
99+
<td><a id="portal" href="/index.html">Portal</a></td>
100+
<td><a href="/ntp/index.html">Clock</a></td>
101+
<td><span>Events</span></td>
102+
</tr>
103+
</table>
104+
</td></tr>
105+
</table>
106+
<h1><span class="hostname"></span></h1>
107+
<table class="housewidetable eventlist" border="0">
108+
<tr>
109+
<th width="15%">Time</th>
110+
<th width="10%">Category</th>
111+
<th width="15%">Name</th>
112+
<th width="15%">Action</th>
113+
<th width="45%">Description</th>
114+
</tr>
115+
</table>
116+
</body>
117+
</html>
118+

public/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
<tr>
6969
<td><a id="portal" href="/index.html">Portal</a></td>
7070
<td><span>Clock</span></td>
71+
<td><a href="/ntp/events.html">Events</a></td>
7172
</tr>
7273
</table>
7374
</td></tr>

0 commit comments

Comments
 (0)