==> src/net.c <==1/* ui.rs's "network" and "7 net" sections, ported. */
2#include "mesh/top_ui_net.h"
3
4#include <string.h>
5
6static const char *human(mt_frame *f, double x) {
7 char b[64];
8 mt_human(x, b, sizeof b);
9 return tui_arena_strdup(f->a, b);
10}
11
12static const char *sh(mt_frame *f, double x) {
13 char b[64];
14 mt_short(x, b, sizeof b);
15 return tui_arena_strdup(f->a, b);
16}
17
18static const char *spark(mt_frame *f, const mt_net_track *n, size_t w) {
19 char *o = tui_arena_alloc(f->a, 3 * w + 1);
20 mt_spark(n->hist, n->nhist, w, o);
21 return o;
22}
23
24static const char *share_text(mt_frame *f, const mt_net_track *n) {
25 double x;
26 return mt_net_mesh_share(n, &x) ? MT_FMT(f, "%s%%", mt_ff(f, x * 100.0, 0)) : "-";
27}
28
29static tui_span dim_if_zero(double x, const char *s) { return mt_fgs(s, x > 0.0 ? MT_FG : MT_DIM); }
30
31size_t mt_net_cells(mt_frame *f, const mt_machine *m, bool wide, tui_table_cell *out) {
32 const mt_net_track *n = &m->net;
33 double rx = mt_nic_rx(&n->total), tx = mt_nic_tx(&n->total);
34 double x;
35 tui_span share = mt_net_mesh_share(n, &x) ? mt_fgs(MT_FMT(f, "%s%%", mt_ff(f, x * 100.0, 0)), MT_ACCENT) : mt_fgs("-", MT_FAINT);
36 size_t k = 0;
37 uint64_t age;
38 if (!mt_net_nic_age_ms(n, &age)) {
39 out[k++] = mt_cell_span(f, mt_fgs("no sample", MT_DIM));
40 out[k++] = mt_cell_str(f, "");
41 } else if (wide) {
42 out[k++] = mt_cell_span(f, dim_if_zero(rx, human(f, rx)));
43 out[k++] = mt_cell_span(f, dim_if_zero(tx, human(f, tx)));
44 } else {
45 out[k++] = mt_cell_str(f, MT_FMT(f, "%s %s", sh(f, rx), sh(f, tx)));
46 }
47 out[k++] = mt_cell_span(f, share);
48 if (wide) out[k++] = mt_cell_span(f, mt_fgs(spark(f, n, MT_OV_SPARK), MT_ACCENT));
49 return k;
50}
51
52/* A member's short name for the network rows (its JOIN hostname). */
53static const char *peer_name(mt_frame *f, const mt_snapshot *s, uint32_t k, size_t w) {
54 mt_member mb = mt_snapshot_member(s, k);
55 const char *h = *mb.host ? mb.host : MT_FMT(f, "system %u", k);
56 return mt_take(f, h, w);
57}
58
59/* `2.0k/1.9k` (out/in frames/s), `-` when both are 0. */
60static const char *frames_pair(mt_frame *f, double out, double inn) {
61 if (out == 0.0 && inn == 0.0) return "-";
62 return MT_FMT(f, "%s/%s", sh(f, out), sh(f, inn));
63}
64
65/* The part of an address before its last ':' (the whole when none). */
66static const char *addr_of(mt_frame *f, const char *local) {
67 const char *c = strrchr(local, ':');
68 return c ? tui_arena_strndup(f->a, local, (size_t)(c - local)) : tui_arena_strdup(f->a, local);
69}
70
71static tui_style bad_style(double x) { return tui_fg(x > 0.0 ? MT_BAD : MT_DIM); }
72
73static tui_line nic_line(mt_frame *f, bool wide, const char *name, const mt_nic_track *t, const tui_span *extra, size_t nextra) {
74 double rx = mt_nic_rx(t), tx = mt_nic_tx(t), pr, pt;
75 mt_nic_packets(t, &pr, &pt);
76 double e = mt_nic_errors(t), d = mt_nic_drops(t);
77 tui_line v;
78 if (wide)
79 v = MT_LINE(f, mt_fgs(mt_pad(f, name, 50, '<'), MT_FG),
80 mt_fgb(MT_FMT(f, "%s%s", mt_pad(f, human(f, rx), 12, '>'), mt_pad(f, human(f, tx), 12, '>')), MT_FG),
81 mt_fgs(MT_FMT(f, " %s", mt_pad(f, MT_FMT(f, "%s/%s", sh(f, pr), sh(f, pt)), 10, '>')), MT_DIM),
82 mt_styled(MT_FMT(f, " pkt/s err %s/s", sh(f, e)), bad_style(e)),
83 mt_styled(MT_FMT(f, " drop %s/s ", sh(f, d)), bad_style(d)));
84 else
85 v = MT_LINE(f, mt_fgs(mt_pad(f, mt_take(f, name, 11), 12, '<'), MT_FG),
86 mt_fgb(MT_FMT(f, "↓%s↑%s", mt_pad(f, sh(f, rx), 6, '<'), mt_pad(f, sh(f, tx), 6, '<')), MT_FG),
87 mt_fgs(MT_FMT(f, "%s/%sp ", sh(f, pr), sh(f, pt)), MT_DIM), mt_styled(MT_FMT(f, "e%s ", sh(f, e)), bad_style(e)),
88 mt_styled(MT_FMT(f, "d%s ", sh(f, d)), bad_style(d)));
89 for (size_t i = 0; i < nextra; i++) tui_line_push(f->a, &v, extra[i]);
90 return v;
91}
92
93void mt_net_lines(mt_frame *f, const mt_machine *m, const mt_snapshot *s, uint16_t width, bool compact, mt_lines *out) {
94 size_t w = width;
95 bool wide = w >= 100 && !compact;
96 const mt_net_track *n = &m->net;
97 uint32_t me;
98 bool has_me = mt_snapshot_system(s, &me);
99 if (wide && has_me) {
100 const char *h = MT_FMT(f, "%s%s%s%s %s%s%s%s%s", mt_pad(f, "link", 20, '<'), mt_pad(f, "interface local address", 30, '<'),
101 mt_pad(f, "rx", 12, '>'), mt_pad(f, "tx", 12, '>'), mt_pad(f, "round o/i", 10, '>'),
102 mt_pad(f, "place o/i", 10, '>'), mt_pad(f, "load", 8, '>'), mt_pad(f, "ctl", 8, '>'), mt_pad(f, "RTT", 8, '>'));
103 mt_lines_push(f, out, tui_line_span(f->a, mt_label(h)));
104 }
105 if (has_me) {
106 uint32_t ns = mt_snapshot_systems(s);
107 for (uint32_t k = 0; k < ns; k++) {
108 if (k == me) continue;
109 mt_member mb = mt_snapshot_member(s, k);
110 mt_link_stat l = mt_snapshot_link(s, k);
111 const mt_link_track *t = mt_net_link(n, k);
112 if (!t) continue;
113 double rx = mt_meter_per_s(&t->rx), tx = mt_meter_per_s(&t->tx);
114 double fr[MESH_NET_KINDS][2];
115 for (size_t i = 0; i < MESH_NET_KINDS; i++) {
116 double r, x;
117 mt_link_frames_per_s(t, i, &r, &x);
118 fr[i][0] = x;
119 fr[i][1] = r;
120 }
121 bool up = mb.state == MESH_MS_UP;
122 tui_style name_st = tui_fg_bold(up ? mt_sys_color(k) : MT_BAD);
123 const char *rtt = up && mb.rtt_us > 0.0f ? mt_fmt_us(f, mb.rtt_us) : "-";
124 const char *iface = *l.iface ? l.iface : "-";
125 const char *addr = addr_of(f, l.local);
126 const char *arrow = MT_FMT(f, "→%u ", k);
127 if (wide) {
128 mt_lines_push(f, out,
129 MT_LINE(f, mt_styled(arrow, name_st), mt_fgs(mt_pad(f, peer_name(f, s, k, 16), 17, '<'), MT_FG),
130 mt_fgs(mt_pad(f, mt_take(f, MT_FMT(f, "%s %s", iface, addr), 29), 30, '<'), MT_DIM),
131 mt_fgb(MT_FMT(f, "%s%s", mt_pad(f, human(f, rx), 12, '>'), mt_pad(f, human(f, tx), 12, '>')), MT_FG),
132 mt_fgs(MT_FMT(f, " %s%s", mt_pad(f, frames_pair(f, fr[0][0], fr[0][1]), 10, '>'),
133 mt_pad(f, frames_pair(f, fr[1][0], fr[1][1]), 10, '>')),
134 MT_WARN),
135 mt_fgs(MT_FMT(f, "%s%s", mt_pad(f, frames_pair(f, fr[2][0], fr[2][1]), 8, '>'),
136 mt_pad(f, frames_pair(f, fr[3][0], fr[3][1]), 8, '>')),
137 MT_DIM),
138 mt_fgs(mt_pad(f, rtt, 8, '>'), MT_ACCENT)));
139 } else if (compact) {
140 mt_lines_push(f, out,
141 MT_LINE(f, mt_styled(arrow, name_st), mt_fgs(mt_pad(f, peer_name(f, s, k, 8), 9, '<'), MT_FG),
142 mt_fgb(MT_FMT(f, "↓%s↑%s", mt_pad(f, sh(f, rx), 6, '<'), mt_pad(f, sh(f, tx), 6, '<')), MT_FG),
143 mt_fgs(MT_FMT(f, "r%s", mt_pad(f, sh(f, fr[0][0]), 6, '<')), MT_WARN), mt_fgs(rtt, MT_ACCENT)));
144 } else {
145 mt_lines_push(f, out,
146 MT_LINE(f, mt_styled(arrow, name_st), mt_fgs(mt_pad(f, peer_name(f, s, k, 15), 16, '<'), MT_FG),
147 mt_fgs(MT_FMT(f, "%s %s", iface, addr), MT_DIM)));
148 tui_line l2 = MT_LINE(f, tui_span_raw(" "),
149 mt_fgb(MT_FMT(f, "↓%s↑%s", mt_pad(f, sh(f, rx), 6, '<'), mt_pad(f, sh(f, tx), 6, '<')), MT_FG),
150 mt_fgs(MT_FMT(f, "r%s ", frames_pair(f, fr[0][0], fr[0][1])), MT_WARN));
151 static const char tag[4] = {0, 'p', 'l', 'c'};
152 for (size_t i = 1; i < 4; i++)
153 tui_line_push(f->a, &l2, mt_fgs(MT_FMT(f, "%c%s ", tag[i], frames_pair(f, fr[i][0], fr[i][1])), MT_DIM));
154 tui_line_push(f->a, &l2, mt_fgs(rtt, MT_ACCENT));
155 mt_lines_push(f, out, l2);
156 }
157 }
158 }
159 /* The NICs that carry links, then the total. */
160 if (!compact) {
161 mt_nic nics[MESH_MAX_NICS];
162 size_t nn = mt_snapshot_nics(s, nics);
163 for (size_t i = 0; i < nn; i++) {
164 const mt_nic *x = &nics[i];
165 const char *name = wide ? MT_FMT(f, "%s (to %s)", x->name, mt_mask_list(f, x->links)) : tui_arena_strdup(f->a, x->name);
166 const mt_nic_track *t = mt_net_nic(n, x->name);
167 if (t && x->count > 0)
168 mt_lines_push(f, out, nic_line(f, wide, name, t, NULL, 0));
169 else
170 mt_lines_push(f, out, MT_LINE(f, mt_fgs(MT_FMT(f, "%s ", x->name), MT_FG), mt_fgs("gone", MT_BAD)));
171 }
172 }
173 mt_nic total = mt_snapshot_net_total(s);
174 const char *share = share_text(f, n);
175 uint64_t age;
176 if (!mt_net_nic_age_ms(n, &age)) {
177 mt_lines_push(f, out, tui_line_span(f->a, mt_fgs("NICs: no sample yet", MT_DIM)));
178 } else {
179 const char *name = wide ? MT_FMT(f, "total (%u interfaces)", total.count) : MT_FMT(f, "total(%u)", total.count);
180 /* Narrow: the mesh share is in the box's title. */
181 tui_span extra[2];
182 size_t ne = 0;
183 if (wide) extra[ne++] = mt_fgs(MT_FMT(f, "mesh %s", share), MT_ACCENT);
184 if (age > 3000) extra[ne++] = mt_fgb(MT_FMT(f, " sample %llus old", (unsigned long long)(age / 1000)), MT_BAD);
185 mt_lines_push(f, out, nic_line(f, wide, name, &n->total, extra, ne));
186 }
187 if (!compact) {
188 const mt_meter *c = n->ctl, *h = n->http;
189 mt_lines_push(f, out,
190 MT_LINE(f, mt_label("ctl "),
191 mt_fgs(MT_FMT(f, "↓%s ↑%s %s/s", sh(f, mt_meter_per_s(&c[0])), sh(f, mt_meter_per_s(&c[1])), sh(f, mt_meter_per_s(&c[2]))), MT_FG),
192 mt_label(wide ? " requests http relay " : " http "),
193 mt_fgs(MT_FMT(f, "↓%s ↑%s %s/s", sh(f, mt_meter_per_s(&h[0])), sh(f, mt_meter_per_s(&h[1])), sh(f, mt_meter_per_s(&h[2]))), MT_FG),
194 mt_label(wide ? " connections" : "")));
195 }
196}
197
198void mt_draw_net_box(mt_frame *f, tui_rect area, const mt_machine *m, const mt_lines *lines) {
199 if (area.height == 0 || area.width == 0) return;
200 const mt_net_track *n = &m->net;
201 const char *share = share_text(f, n);
202 tui_line title = MT_LINE(f, mt_fgb(" Network ", MT_FG),
203 mt_label(MT_FMT(f, "↓%s ↑%s B/s ", sh(f, mt_nic_rx(&n->total)), sh(f, mt_nic_tx(&n->total)))),
204 mt_fgs(MT_FMT(f, "mesh %s ", share), MT_ACCENT));
205 if (area.width >= 60) tui_line_push(f->a, &title, mt_label("frames/s out/in "));
206 tui_block b = tui_block_bordered();
207 b.set = &TUI_BORDER_ROUNDED;
208 b.border_style = tui_fg(MT_FAINT);
209 tui_block_add_title(f->a, &b, title, TUI_TITLE_DEFAULT);
210 tui_rect inner = tui_block_inner(&b, area);
211 mt_block(f, &b, area);
212 mt_par(f, mt_text(f, lines), inner);
213}
214
215/* System k's hostname from its own snapshot (13 characters at most). */
216static const char *sys_host(mt_frame *f, const mt_machine *const *by_sys, uint32_t k) {
217 return by_sys[k] ? mt_take(f, mt_name(f, by_sys[k]), 13) : "";
218}
219
220typedef struct {
221 bool has_tx, has_rx;
222 double tx, rx;
223} dir_rates;
224
225/* Directed link k -> j: (sender's tx, receiver's rx), bytes/s. */
226static dir_rates dir(const mt_machine *const *by_sys, uint32_t k, uint32_t j) {
227 dir_rates d = {0};
228 const mt_link_track *t;
229 if (by_sys[k] && (t = mt_net_link(&by_sys[k]->net, j))) {
230 d.has_tx = true;
231 d.tx = mt_meter_per_s(&t->tx);
232 }
233 if (by_sys[j] && (t = mt_net_link(&by_sys[j]->net, k))) {
234 d.has_rx = true;
235 d.rx = mt_meter_per_s(&t->rx);
236 }
237 return d;
238}
239
240static void table_in(mt_frame *f, const mt_rows *rows, tui_table_row *hdr, const tui_constraint *w, size_t nw, tui_line title, tui_rect area) {
241 tui_block b = mt_panel(f, title, MT_FAINT);
242 tui_table t = tui_table_new(rows->rows, rows->n, w, nw);
243 t.header = hdr;
244 t.column_spacing = 1;
245 t.block = &b;
246 mt_table(f, &t, area);
247}
248
249void mt_draw_net(mt_frame *f, tui_rect area, const mt_app *app) {
250 uint32_t n = mt_app_cluster_size(app);
251 const mt_machine **by_sys = tui_arena_alloc(f->a, (n ? n : 1) * sizeof *by_sys);
252 for (uint32_t k = 0; k < n; k++)
253 for (size_t i = 0; i < app->nmachines; i++) {
254 uint32_t sk;
255 if (mt_machine_system(&app->machines[i], &sk) && sk == k) {
256 by_sys[k] = &app->machines[i];
257 break;
258 }
259 }
260 bool clustered = false;
261 for (size_t i = 0; i < app->nmachines; i++) {
262 uint32_t sk;
263 clustered |= mt_machine_system(&app->machines[i], &sk);
264 }
265 uint16_t links_h = clustered ? (uint16_t)(n * (n ? n - 1 : 0) + 3) : 0;
266 uint16_t matrix_h = clustered ? (uint16_t)(n + 4) : 0;
267 size_t nic_rows = 0;
268 for (size_t i = 0; i < app->nmachines; i++) {
269 const mt_machine *m = &app->machines[i];
270 if (m->has_snap) {
271 mt_nic tmp[MESH_MAX_NICS];
272 nic_rows += mt_snapshot_nics(&m->snap, tmp) + 2;
273 } else {
274 nic_rows += 1;
275 }
276 }
277 tui_rect r[3];
278 tui_constraint cons[3] = {TUI_LENGTH(matrix_h), TUI_LENGTH(links_h), TUI_MIN(nic_rows < 3 ? nic_rows : 3)};
279 tui_vsplit(area, 3, cons, r);
280 tui_rect mx = r[0], links = r[1], nics = r[2];
281 if (clustered && mx.height > 0) {
282 tui_table_row header = tui_table_row_new();
283 tui_table_row_push(f->a, &header, mt_cell_span(f, mt_label("from \\ to")));
284 for (uint32_t j = 0; j < n; j++)
285 tui_table_row_push(f->a, &header, mt_cell_span(f, mt_fgb(MT_FMT(f, "%u %s", j, sys_host(f, by_sys, j)), mt_sys_color(j))));
286 tui_table_row_push(f->a, &header, mt_cell_span(f, mt_label("sent, all links")));
287 mt_rows rows = {0};
288 for (uint32_t k = 0; k < n; k++) {
289 tui_table_row row = tui_table_row_new();
290 tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgb(MT_FMT(f, "%u %s", k, sys_host(f, by_sys, k)), mt_sys_color(k))));
291 double sum = 0.0;
292 for (uint32_t j = 0; j < n; j++) {
293 if (j == k) {
294 tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs("·", MT_FAINT)));
295 continue;
296 }
297 dir_rates d = dir(by_sys, k, j);
298 sum += d.has_tx ? d.tx : 0.0;
299 const char *t = d.has_tx ? sh(f, d.tx) : "?";
300 const char *rr = d.has_rx ? sh(f, d.rx) : "?";
301 tui_table_row_push(f->a, &row,
302 mt_cell_line(f, MT_LINE(f, mt_fgb(mt_pad(f, t, 6, '>'), MT_FG), mt_fgs(MT_FMT(f, " / %s", mt_pad(f, rr, 6, '<')), MT_DIM))));
303 }
304 tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs(human(f, sum), MT_ACCENT)));
305 mt_rows_push(f, &rows, row);
306 }
307 tui_constraint *w = tui_arena_alloc(f->a, (n + 2) * sizeof *w);
308 w[0] = TUI_LENGTH(16);
309 for (uint32_t j = 0; j < n; j++) w[1 + j] = TUI_LENGTH(16);
310 w[n + 1] = TUI_FILL(1);
311 tui_line title = MT_LINE(f, mt_fgb(" link matrix ", MT_FG),
312 mt_label("bytes/s: sent (the sender's count) / received (the receiver's count) "));
313 table_in(f, &rows, &header, w, n + 2, title, mx);
314 }
315 if (clustered && links.height > 0) {
316 static const char *const header[9] = {"link", "via (sender)", "sent", "received", "round/s", "place/s", "load/s", "ctl/s", "RTT"};
317 mt_rows rows = {0};
318 for (uint32_t k = 0; k < n; k++)
319 for (uint32_t j = 0; j < n; j++) {
320 if (j == k) continue;
321 dir_rates d = dir(by_sys, k, j);
322 const mt_machine *sender = by_sys[k];
323 const mt_link_track *t = sender ? mt_net_link(&sender->net, j) : NULL;
324 const mt_snapshot *st = sender && sender->has_snap ? &sender->snap : NULL;
325 const char *via = "-", *rtt = "-";
326 if (st) {
327 mt_link_stat l = mt_snapshot_link(st, j);
328 via = MT_FMT(f, "%s %s", l.iface, addr_of(f, l.local));
329 float r = mt_snapshot_member(st, j).rtt_us;
330 if (r > 0.0f) rtt = mt_fmt_us(f, r);
331 }
332#define FR(i) (t ? sh(f, mt_meter_per_s(&t->tx_frames[i])) : "?")
333 tui_table_cell c[9] = {
334 mt_cell_line(f, MT_LINE(f, mt_fgb(MT_FMT(f, "%u", k), mt_sys_color(k)), mt_label(" → "), mt_fgb(MT_FMT(f, "%u", j), mt_sys_color(j)))),
335 mt_cell_span(f, mt_fgs(via, MT_DIM)),
336 mt_cell_span(f, mt_fgb(d.has_tx ? human(f, d.tx) : "?", MT_FG)),
337 mt_cell_span(f, mt_fgs(d.has_rx ? human(f, d.rx) : "?", MT_DIM)),
338 mt_cell_span(f, mt_fgs(FR(MESH_KIND_ROUND), MT_WARN)),
339 mt_cell_str(f, FR(MESH_KIND_PLACE)),
340 mt_cell_str(f, FR(MESH_KIND_LOAD)),
341 mt_cell_str(f, FR(MESH_KIND_CTL)),
342 mt_cell_span(f, mt_fgs(rtt, MT_ACCENT)),
343 };
344#undef FR
345 mt_rows_push(f, &rows, mt_row(f, c, 9));
346 }
347 static const tui_constraint w[9] = {
348 {TUI_C_LENGTH, 7, 0}, {TUI_C_LENGTH, 32, 0}, {TUI_C_LENGTH, 11, 0}, {TUI_C_LENGTH, 11, 0}, {TUI_C_LENGTH, 8, 0},
349 {TUI_C_LENGTH, 8, 0}, {TUI_C_LENGTH, 7, 0}, {TUI_C_LENGTH, 6, 0}, {TUI_C_FILL, 1, 0},
350 };
351 tui_table_row hdr = mt_header(f, header, 9);
352 table_in(f, &rows, &hdr, w, 9, MT_LINE(f, mt_fgb(" links ", MT_FG), mt_label("frames/s as sent ")), links);
353 }
354 if (nics.height == 0) return;
355 static const char *const header[10] = {"machine", "interface", "to sys", "rx", "tx", "pkt/s in/out", "err/s", "drop/s", "mesh", "rx+tx, 1 s/col"};
356 mt_rows rows = {0};
357 for (size_t i = 0; i < app->nmachines; i++) {
358 const mt_machine *m = &app->machines[i];
359 tui_span name = mt_fgb(mt_name(f, m), MT_MACHINE[i % 8]);
360 if (!m->has_snap) {
361 tui_table_cell c[2] = {mt_cell_span(f, name), mt_cell_span(f, mt_fgs(m->status, MT_BAD))};
362 mt_rows_push(f, &rows, mt_row(f, c, 2));
363 continue;
364 }
365 bool first = true;
366 mt_nic xs[MESH_MAX_NICS];
367 size_t nx = mt_snapshot_nics(&m->snap, xs);
368 for (size_t q = 0; q <= nx; q++) {
369 const char *iface, *lk;
370 const mt_nic_track *t;
371 if (q < nx) {
372 iface = tui_arena_strdup(f->a, xs[q].name);
373 lk = mt_mask_list(f, xs[q].links);
374 t = xs[q].count > 0 ? mt_net_nic(&m->net, xs[q].name) : NULL;
375 } else {
376 iface = MT_FMT(f, "total (%u)", mt_snapshot_net_total(&m->snap).count);
377 lk = "";
378 t = &m->net.total;
379 }
380 tui_table_row row = tui_table_row_new();
381 tui_table_row_push(f->a, &row, mt_cell_span(f, first ? name : tui_span_raw("")));
382 first = false;
383 tui_table_row_push(f->a, &row, mt_cell_str(f, iface));
384 tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs(lk, MT_DIM)));
385 if (t) {
386 double pr, pt;
387 mt_nic_packets(t, &pr, &pt);
388 double e = mt_nic_errors(t), d = mt_nic_drops(t);
389 tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgb(human(f, mt_nic_rx(t)), MT_FG)));
390 tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgb(human(f, mt_nic_tx(t)), MT_FG)));
391 tui_table_row_push(f->a, &row, mt_cell_str(f, MT_FMT(f, "%s/%s", sh(f, pr), sh(f, pt))));
392 tui_table_row_push(f->a, &row, mt_cell_span(f, mt_styled(sh(f, e), bad_style(e))));
393 tui_table_row_push(f->a, &row, mt_cell_span(f, mt_styled(sh(f, d), bad_style(d))));
394 } else {
395 tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs("gone", MT_BAD)));
396 }
397 if (q == nx) {
398 tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs(share_text(f, &m->net), MT_ACCENT)));
399 tui_table_row_push(f->a, &row, mt_cell_span(f, mt_fgs(spark(f, &m->net, 30), MT_ACCENT)));
400 }
401 mt_rows_push(f, &rows, row);
402 }
403 const mt_meter *c = m->net.ctl, *h = m->net.http;
404 tui_table_cell cc[6] = {
405 mt_cell_str(f, ""),
406 mt_cell_span(f, mt_label("ctl / http")),
407 mt_cell_str(f, ""),
408 mt_cell_span(f, mt_fgs(MT_FMT(f, "%s / %s", sh(f, mt_meter_per_s(&c[0])), sh(f, mt_meter_per_s(&h[0]))), MT_DIM)),
409 mt_cell_span(f, mt_fgs(MT_FMT(f, "%s / %s", sh(f, mt_meter_per_s(&c[1])), sh(f, mt_meter_per_s(&h[1]))), MT_DIM)),
410 mt_cell_span(f, mt_fgs(MT_FMT(f, "%s req, %s conn/s", sh(f, mt_meter_per_s(&c[2])), sh(f, mt_meter_per_s(&h[2]))), MT_DIM)),
411 };
412 mt_rows_push(f, &rows, mt_row(f, cc, 6));
413 }
414 static const tui_constraint w[10] = {
415 {TUI_C_LENGTH, 16, 0}, {TUI_C_LENGTH, 16, 0}, {TUI_C_LENGTH, 6, 0}, {TUI_C_LENGTH, 11, 0}, {TUI_C_LENGTH, 11, 0},
416 {TUI_C_LENGTH, 19, 0}, {TUI_C_LENGTH, 6, 0}, {TUI_C_LENGTH, 6, 0}, {TUI_C_LENGTH, 5, 0}, {TUI_C_FILL, 1, 0},
417 };
418 tui_table_row hdr = mt_header(f, header, 10);
419 tui_line title = MT_LINE(f, mt_fgb(" interfaces ", MT_FG),
420 mt_label("the ones carrying peer links, and each machine's total (no loopback, container, VM or tunnel interfaces) "));
421 table_in(f, &rows, &hdr, w, 10, title, nics);
422}