Line | Count | Source |
1 | | /* $OpenBSD: monitor.c,v 1.7 2026/07/27 19:15:58 nicm Exp $ */ |
2 | | |
3 | | /* |
4 | | * Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com> |
5 | | * |
6 | | * Permission to use, copy, modify, and distribute this software for any |
7 | | * purpose with or without fee is hereby granted, provided that the above |
8 | | * copyright notice and this permission notice appear in all copies. |
9 | | * |
10 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER |
15 | | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
16 | | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | | */ |
18 | | |
19 | | #include <sys/types.h> |
20 | | |
21 | | #include <event.h> |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | |
25 | | #include "tmux.h" |
26 | | |
27 | | /* Subscription pane. */ |
28 | | struct monitor_pane { |
29 | | u_int pane; |
30 | | u_int idx; |
31 | | char *last; |
32 | | u_int generation; |
33 | | |
34 | | RB_ENTRY(monitor_pane) entry; |
35 | | }; |
36 | | RB_HEAD(monitor_panes, monitor_pane); |
37 | | |
38 | | /* Subscription window. */ |
39 | | struct monitor_window { |
40 | | u_int window; |
41 | | u_int idx; |
42 | | char *last; |
43 | | u_int generation; |
44 | | |
45 | | RB_ENTRY(monitor_window) entry; |
46 | | }; |
47 | | RB_HEAD(monitor_windows, monitor_window); |
48 | | |
49 | | /* Subscription. */ |
50 | | struct monitor_item { |
51 | | char *name; |
52 | | char *format; |
53 | | |
54 | | enum monitor_type type; |
55 | | u_int id; |
56 | | int flags; |
57 | | |
58 | | char *last; |
59 | | struct monitor_panes panes; |
60 | | struct monitor_windows windows; |
61 | | |
62 | | u_int fire_count; |
63 | | time_t fire_time; |
64 | | |
65 | | RB_ENTRY(monitor_item) entry; |
66 | | }; |
67 | | RB_HEAD(monitor_items, monitor_item); |
68 | | |
69 | | /* Monitored subscription set. */ |
70 | | struct monitor_set { |
71 | | struct client *client; |
72 | | struct session *session; |
73 | | monitor_cb cb; |
74 | | void *data; |
75 | | |
76 | | struct monitor_items items; |
77 | | struct event timer; |
78 | | u_int generation; |
79 | | }; |
80 | | |
81 | | static void monitor_timer(__unused int, __unused short, void *); |
82 | | |
83 | | /* Get the session for this monitor set. */ |
84 | | static struct session * |
85 | | monitor_get_session(struct monitor_set *ms) |
86 | 0 | { |
87 | 0 | struct session *s; |
88 | |
|
89 | 0 | if (ms->client != NULL) |
90 | 0 | return (ms->client->session); |
91 | 0 | s = ms->session; |
92 | 0 | if (s == NULL) |
93 | 0 | return (RB_MIN(sessions, &sessions)); |
94 | 0 | if (session_find_by_id(s->id) != s) |
95 | 0 | return (NULL); |
96 | 0 | return (s); |
97 | 0 | } |
98 | | |
99 | | /* Create a format tree for a subscription. */ |
100 | | static struct format_tree * |
101 | | monitor_create_formats(struct client *c, struct session *s, struct winlink *wl, |
102 | | struct window_pane *wp) |
103 | 0 | { |
104 | 0 | struct format_tree *ft; |
105 | |
|
106 | 0 | ft = format_create(NULL, NULL, 0, FORMAT_NOJOBS); |
107 | 0 | format_defaults(ft, c, s, wl, wp); |
108 | 0 | return (ft); |
109 | 0 | } |
110 | | |
111 | | /* Compare subscriptions. */ |
112 | | static int |
113 | | monitor_item_cmp(struct monitor_item *m1, struct monitor_item *m2) |
114 | 0 | { |
115 | 0 | return (strcmp(m1->name, m2->name)); |
116 | 0 | } |
117 | 0 | RB_GENERATE_STATIC(monitor_items, monitor_item, entry, monitor_item_cmp); Unexecuted instantiation: monitor.c:monitor_items_RB_MINMAX Unexecuted instantiation: monitor.c:monitor_items_RB_REMOVE Unexecuted instantiation: monitor.c:monitor_items_RB_REMOVE_COLOR Unexecuted instantiation: monitor.c:monitor_items_RB_FIND Unexecuted instantiation: monitor.c:monitor_items_RB_INSERT |
118 | 0 |
|
119 | 0 | /* Compare subscription panes. */ |
120 | 0 | static int |
121 | 0 | monitor_pane_cmp(struct monitor_pane *mp1, struct monitor_pane *mp2) |
122 | 0 | { |
123 | 0 | if (mp1->pane < mp2->pane) |
124 | 0 | return (-1); |
125 | 0 | if (mp1->pane > mp2->pane) |
126 | 0 | return (1); |
127 | 0 | if (mp1->idx < mp2->idx) |
128 | 0 | return (-1); |
129 | 0 | if (mp1->idx > mp2->idx) |
130 | 0 | return (1); |
131 | 0 | return (0); |
132 | 0 | } |
133 | 0 | RB_GENERATE_STATIC(monitor_panes, monitor_pane, entry, monitor_pane_cmp); Unexecuted instantiation: monitor.c:monitor_panes_RB_MINMAX Unexecuted instantiation: monitor.c:monitor_panes_RB_REMOVE Unexecuted instantiation: monitor.c:monitor_panes_RB_REMOVE_COLOR Unexecuted instantiation: monitor.c:monitor_panes_RB_FIND Unexecuted instantiation: monitor.c:monitor_panes_RB_INSERT |
134 | 0 |
|
135 | 0 | /* Compare subscription windows. */ |
136 | 0 | static int |
137 | 0 | monitor_window_cmp(struct monitor_window *mw1, struct monitor_window *mw2) |
138 | 0 | { |
139 | 0 | if (mw1->window < mw2->window) |
140 | 0 | return (-1); |
141 | 0 | if (mw1->window > mw2->window) |
142 | 0 | return (1); |
143 | 0 | if (mw1->idx < mw2->idx) |
144 | 0 | return (-1); |
145 | 0 | if (mw1->idx > mw2->idx) |
146 | 0 | return (1); |
147 | 0 | return (0); |
148 | 0 | } |
149 | 0 | RB_GENERATE_STATIC(monitor_windows, monitor_window, entry, monitor_window_cmp); Unexecuted instantiation: monitor.c:monitor_windows_RB_MINMAX Unexecuted instantiation: monitor.c:monitor_windows_RB_REMOVE Unexecuted instantiation: monitor.c:monitor_windows_RB_REMOVE_COLOR Unexecuted instantiation: monitor.c:monitor_windows_RB_FIND Unexecuted instantiation: monitor.c:monitor_windows_RB_INSERT |
150 | 0 |
|
151 | 0 | /* Free a subscription. */ |
152 | 0 | static void |
153 | 0 | monitor_free_item(struct monitor_set *ms, struct monitor_item *me) |
154 | 0 | { |
155 | 0 | struct monitor_pane *mp, *mp1; |
156 | 0 | struct monitor_window *mw, *mw1; |
157 | |
|
158 | 0 | RB_FOREACH_SAFE(mp, monitor_panes, &me->panes, mp1) { |
159 | 0 | RB_REMOVE(monitor_panes, &me->panes, mp); |
160 | 0 | free(mp->last); |
161 | 0 | free(mp); |
162 | 0 | } |
163 | 0 | RB_FOREACH_SAFE(mw, monitor_windows, &me->windows, mw1) { |
164 | 0 | RB_REMOVE(monitor_windows, &me->windows, mw); |
165 | 0 | free(mw->last); |
166 | 0 | free(mw); |
167 | 0 | } |
168 | 0 | free(me->last); |
169 | |
|
170 | 0 | RB_REMOVE(monitor_items, &ms->items, me); |
171 | 0 | free(me->name); |
172 | 0 | free(me->format); |
173 | 0 | free(me); |
174 | 0 | } |
175 | | |
176 | | /* Report a changed value. */ |
177 | | static void |
178 | | monitor_report(struct monitor_set *ms, struct monitor_item *me, |
179 | | struct session *s, struct winlink *wl, struct window_pane *wp, |
180 | | const char *value, const char *last) |
181 | 0 | { |
182 | 0 | struct monitor_change change = { 0 }; |
183 | |
|
184 | 0 | log_debug("%s: %s changed to %s", __func__, me->name, value); |
185 | |
|
186 | 0 | me->fire_count++; |
187 | 0 | me->fire_time = current_time; |
188 | |
|
189 | 0 | change.name = me->name; |
190 | 0 | change.value = value; |
191 | 0 | change.last = last; |
192 | 0 | change.c = ms->client; |
193 | 0 | change.s = s; |
194 | 0 | change.wl = wl; |
195 | 0 | change.wp = wp; |
196 | 0 | ms->cb(&change, ms->data); |
197 | 0 | } |
198 | | |
199 | | /* Check a value against its last value and report if changed. */ |
200 | | static void |
201 | | monitor_check_value(struct monitor_set *ms, struct monitor_item *me, |
202 | | struct session *s, struct winlink *wl, struct window_pane *wp, |
203 | | char *value, char **last) |
204 | 0 | { |
205 | 0 | if (*last == NULL) { |
206 | 0 | *last = value; |
207 | 0 | if ((me->flags & MONITOR_NOTIFY_INITIAL) && |
208 | 0 | ((~me->flags & MONITOR_NOTIFY_TRUE) || |
209 | 0 | format_true(value))) |
210 | 0 | monitor_report(ms, me, s, wl, wp, value, NULL); |
211 | 0 | return; |
212 | 0 | } |
213 | | |
214 | 0 | if (strcmp(value, *last) == 0) { |
215 | 0 | free(value); |
216 | 0 | return; |
217 | 0 | } |
218 | | |
219 | 0 | if ((~me->flags & MONITOR_NOTIFY_TRUE) || format_true(value)) |
220 | 0 | monitor_report(ms, me, s, wl, wp, value, *last); |
221 | 0 | free(*last); |
222 | 0 | *last = value; |
223 | 0 | } |
224 | | |
225 | | /* Check session subscription. */ |
226 | | static void |
227 | | monitor_check_session(struct monitor_set *ms, struct monitor_item *me, |
228 | | struct format_tree *ft) |
229 | 0 | { |
230 | 0 | struct session *s = monitor_get_session(ms); |
231 | 0 | char *value; |
232 | |
|
233 | 0 | value = format_expand(ft, me->format); |
234 | |
|
235 | 0 | monitor_check_value(ms, me, s, NULL, NULL, value, &me->last); |
236 | 0 | } |
237 | | |
238 | | /* Check pane subscription. */ |
239 | | static void |
240 | | monitor_check_pane(struct monitor_set *ms, struct monitor_item *me) |
241 | 0 | { |
242 | 0 | struct client *c = ms->client; |
243 | 0 | struct session *s = monitor_get_session(ms); |
244 | 0 | struct window_pane *wp; |
245 | 0 | struct window *w; |
246 | 0 | struct winlink *wl; |
247 | 0 | struct format_tree *ft; |
248 | 0 | char *value; |
249 | 0 | struct monitor_pane *mp, find; |
250 | |
|
251 | 0 | wp = window_pane_find_by_id(me->id); |
252 | 0 | if (wp == NULL || wp->fd == -1) |
253 | 0 | return; |
254 | 0 | w = wp->window; |
255 | |
|
256 | 0 | TAILQ_FOREACH(wl, &w->winlinks, wentry) { |
257 | 0 | if (wl->session != s) |
258 | 0 | continue; |
259 | | |
260 | 0 | ft = monitor_create_formats(c, s, wl, wp); |
261 | 0 | value = format_expand(ft, me->format); |
262 | 0 | format_free(ft); |
263 | |
|
264 | 0 | find.pane = wp->id; |
265 | 0 | find.idx = wl->idx; |
266 | 0 | mp = RB_FIND(monitor_panes, &me->panes, &find); |
267 | 0 | if (mp == NULL) { |
268 | 0 | mp = xcalloc(1, sizeof *mp); |
269 | 0 | mp->pane = wp->id; |
270 | 0 | mp->idx = wl->idx; |
271 | 0 | RB_INSERT(monitor_panes, &me->panes, mp); |
272 | 0 | } |
273 | |
|
274 | 0 | monitor_check_value(ms, me, s, wl, wp, value, &mp->last); |
275 | 0 | } |
276 | 0 | } |
277 | | |
278 | | /* Check one all-panes subscription. */ |
279 | | static void |
280 | | monitor_check_all_panes_one(struct monitor_set *ms, struct monitor_item *me, |
281 | | struct format_tree *ft, struct winlink *wl, struct window_pane *wp) |
282 | 0 | { |
283 | 0 | struct session *s = monitor_get_session(ms); |
284 | 0 | char *value; |
285 | 0 | struct monitor_pane *mp, find; |
286 | |
|
287 | 0 | value = format_expand(ft, me->format); |
288 | |
|
289 | 0 | find.pane = wp->id; |
290 | 0 | find.idx = wl->idx; |
291 | 0 | mp = RB_FIND(monitor_panes, &me->panes, &find); |
292 | 0 | if (mp == NULL) { |
293 | 0 | mp = xcalloc(1, sizeof *mp); |
294 | 0 | mp->pane = wp->id; |
295 | 0 | mp->idx = wl->idx; |
296 | 0 | RB_INSERT(monitor_panes, &me->panes, mp); |
297 | 0 | } |
298 | 0 | mp->generation = ms->generation; |
299 | |
|
300 | 0 | monitor_check_value(ms, me, s, wl, wp, value, &mp->last); |
301 | 0 | } |
302 | | |
303 | | /* Remove all-panes entries not seen during the current scan. */ |
304 | | static void |
305 | | monitor_sweep_all_panes(struct monitor_item *me, u_int generation) |
306 | 0 | { |
307 | 0 | struct monitor_pane *mp, *mp1; |
308 | |
|
309 | 0 | RB_FOREACH_SAFE(mp, monitor_panes, &me->panes, mp1) { |
310 | 0 | if (mp->generation == generation) |
311 | 0 | continue; |
312 | 0 | RB_REMOVE(monitor_panes, &me->panes, mp); |
313 | 0 | free(mp->last); |
314 | 0 | free(mp); |
315 | 0 | } |
316 | 0 | } |
317 | | |
318 | | /* Check window subscription. */ |
319 | | static void |
320 | | monitor_check_window(struct monitor_set *ms, struct monitor_item *me) |
321 | 0 | { |
322 | 0 | struct client *c = ms->client; |
323 | 0 | struct session *s = monitor_get_session(ms); |
324 | 0 | struct window *w; |
325 | 0 | struct winlink *wl; |
326 | 0 | struct format_tree *ft; |
327 | 0 | char *value; |
328 | 0 | struct monitor_window *mw, find; |
329 | |
|
330 | 0 | w = window_find_by_id(me->id); |
331 | 0 | if (w == NULL) |
332 | 0 | return; |
333 | | |
334 | 0 | TAILQ_FOREACH(wl, &w->winlinks, wentry) { |
335 | 0 | if (wl->session != s) |
336 | 0 | continue; |
337 | | |
338 | 0 | ft = monitor_create_formats(c, s, wl, NULL); |
339 | 0 | value = format_expand(ft, me->format); |
340 | 0 | format_free(ft); |
341 | |
|
342 | 0 | find.window = w->id; |
343 | 0 | find.idx = wl->idx; |
344 | 0 | mw = RB_FIND(monitor_windows, &me->windows, &find); |
345 | 0 | if (mw == NULL) { |
346 | 0 | mw = xcalloc(1, sizeof *mw); |
347 | 0 | mw->window = w->id; |
348 | 0 | mw->idx = wl->idx; |
349 | 0 | RB_INSERT(monitor_windows, &me->windows, mw); |
350 | 0 | } |
351 | |
|
352 | 0 | monitor_check_value(ms, me, s, wl, NULL, value, &mw->last); |
353 | 0 | } |
354 | 0 | } |
355 | | |
356 | | /* Check one all-windows subscription. */ |
357 | | static void |
358 | | monitor_check_all_windows_one(struct monitor_set *ms, struct monitor_item *me, |
359 | | struct format_tree *ft, struct winlink *wl) |
360 | 0 | { |
361 | 0 | struct session *s = monitor_get_session(ms); |
362 | 0 | struct window *w = wl->window; |
363 | 0 | char *value; |
364 | 0 | struct monitor_window *mw, find; |
365 | |
|
366 | 0 | value = format_expand(ft, me->format); |
367 | |
|
368 | 0 | find.window = w->id; |
369 | 0 | find.idx = wl->idx; |
370 | 0 | mw = RB_FIND(monitor_windows, &me->windows, &find); |
371 | 0 | if (mw == NULL) { |
372 | 0 | mw = xcalloc(1, sizeof *mw); |
373 | 0 | mw->window = w->id; |
374 | 0 | mw->idx = wl->idx; |
375 | 0 | RB_INSERT(monitor_windows, &me->windows, mw); |
376 | 0 | } |
377 | 0 | mw->generation = ms->generation; |
378 | |
|
379 | 0 | monitor_check_value(ms, me, s, wl, NULL, value, &mw->last); |
380 | 0 | } |
381 | | |
382 | | /* Remove all-windows entries not seen during the current scan. */ |
383 | | static void |
384 | | monitor_sweep_all_windows(struct monitor_item *me, u_int generation) |
385 | 0 | { |
386 | 0 | struct monitor_window *mw, *mw1; |
387 | |
|
388 | 0 | RB_FOREACH_SAFE(mw, monitor_windows, &me->windows, mw1) { |
389 | 0 | if (mw->generation == generation) |
390 | 0 | continue; |
391 | 0 | RB_REMOVE(monitor_windows, &me->windows, mw); |
392 | 0 | free(mw->last); |
393 | 0 | free(mw); |
394 | 0 | } |
395 | 0 | } |
396 | | |
397 | | /* Check session subscriptions. */ |
398 | | static void |
399 | | monitor_check_sessions(struct monitor_set *ms) |
400 | 0 | { |
401 | 0 | struct client *c = ms->client; |
402 | 0 | struct session *s = monitor_get_session(ms); |
403 | 0 | struct monitor_item *me, *me1; |
404 | 0 | struct format_tree *ft; |
405 | |
|
406 | 0 | ft = monitor_create_formats(c, s, NULL, NULL); |
407 | 0 | RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) { |
408 | 0 | if (me->type == MONITOR_SESSION) |
409 | 0 | monitor_check_session(ms, me, ft); |
410 | 0 | } |
411 | 0 | format_free(ft); |
412 | 0 | } |
413 | | |
414 | | /* Check pane and window subscriptions. */ |
415 | | static void |
416 | | monitor_check_panes_windows(struct monitor_set *ms) |
417 | 0 | { |
418 | 0 | struct monitor_item *me, *me1; |
419 | |
|
420 | 0 | RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) { |
421 | 0 | switch (me->type) { |
422 | 0 | case MONITOR_PANE: |
423 | 0 | monitor_check_pane(ms, me); |
424 | 0 | break; |
425 | 0 | case MONITOR_WINDOW: |
426 | 0 | monitor_check_window(ms, me); |
427 | 0 | break; |
428 | 0 | case MONITOR_SESSION: |
429 | 0 | case MONITOR_ALL_PANES: |
430 | 0 | case MONITOR_ALL_WINDOWS: |
431 | 0 | break; |
432 | 0 | } |
433 | 0 | } |
434 | 0 | } |
435 | | |
436 | | /* Check all-panes subscriptions. */ |
437 | | static void |
438 | | monitor_check_all_panes(struct monitor_set *ms) |
439 | 0 | { |
440 | 0 | struct client *c = ms->client; |
441 | 0 | struct session *s = monitor_get_session(ms); |
442 | 0 | struct monitor_item *me, *me1; |
443 | 0 | struct window_pane *wp; |
444 | 0 | struct format_tree *ft; |
445 | 0 | struct winlink *wl; |
446 | |
|
447 | 0 | if (++ms->generation == 0) |
448 | 0 | ms->generation = 1; |
449 | 0 | RB_FOREACH(wl, winlinks, &s->windows) { |
450 | 0 | TAILQ_FOREACH(wp, &wl->window->panes, entry) { |
451 | 0 | ft = monitor_create_formats(c, s, wl, wp); |
452 | 0 | RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) { |
453 | 0 | if (me->type != MONITOR_ALL_PANES) |
454 | 0 | continue; |
455 | 0 | monitor_check_all_panes_one(ms, me, ft, wl, wp); |
456 | 0 | } |
457 | 0 | format_free(ft); |
458 | 0 | } |
459 | 0 | } |
460 | 0 | RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) { |
461 | 0 | if (me->type == MONITOR_ALL_PANES) |
462 | 0 | monitor_sweep_all_panes(me, ms->generation); |
463 | 0 | } |
464 | 0 | } |
465 | | |
466 | | /* Check all-windows subscriptions. */ |
467 | | static void |
468 | | monitor_check_all_windows(struct monitor_set *ms) |
469 | 0 | { |
470 | 0 | struct client *c = ms->client; |
471 | 0 | struct session *s = monitor_get_session(ms); |
472 | 0 | struct monitor_item *me, *me1; |
473 | 0 | struct format_tree *ft; |
474 | 0 | struct winlink *wl; |
475 | |
|
476 | 0 | if (++ms->generation == 0) |
477 | 0 | ms->generation = 1; |
478 | 0 | RB_FOREACH(wl, winlinks, &s->windows) { |
479 | 0 | ft = monitor_create_formats(c, s, wl, NULL); |
480 | 0 | RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) { |
481 | 0 | if (me->type != MONITOR_ALL_WINDOWS) |
482 | 0 | continue; |
483 | 0 | monitor_check_all_windows_one(ms, me, ft, wl); |
484 | 0 | } |
485 | 0 | format_free(ft); |
486 | 0 | } |
487 | 0 | RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) { |
488 | 0 | if (me->type == MONITOR_ALL_WINDOWS) |
489 | 0 | monitor_sweep_all_windows(me, ms->generation); |
490 | 0 | } |
491 | 0 | } |
492 | | |
493 | | /* Check subscriptions. */ |
494 | | static void |
495 | | monitor_timer(__unused int fd, __unused short events, void *data) |
496 | 0 | { |
497 | 0 | struct monitor_set *ms = data; |
498 | 0 | struct monitor_item *me; |
499 | 0 | struct timeval tv = { .tv_sec = 1 }; |
500 | 0 | int have_session = 0, have_all_panes = 0; |
501 | 0 | int have_all_windows = 0; |
502 | |
|
503 | 0 | log_debug("%s: timer fired", __func__); |
504 | 0 | evtimer_add(&ms->timer, &tv); |
505 | |
|
506 | 0 | if (monitor_get_session(ms) == NULL) |
507 | 0 | return; |
508 | | |
509 | 0 | RB_FOREACH(me, monitor_items, &ms->items) { |
510 | 0 | switch (me->type) { |
511 | 0 | case MONITOR_SESSION: |
512 | 0 | have_session = 1; |
513 | 0 | break; |
514 | 0 | case MONITOR_ALL_PANES: |
515 | 0 | have_all_panes = 1; |
516 | 0 | break; |
517 | 0 | case MONITOR_ALL_WINDOWS: |
518 | 0 | have_all_windows = 1; |
519 | 0 | break; |
520 | 0 | case MONITOR_PANE: |
521 | 0 | case MONITOR_WINDOW: |
522 | 0 | break; |
523 | 0 | } |
524 | 0 | } |
525 | | |
526 | 0 | if (have_session) |
527 | 0 | monitor_check_sessions(ms); |
528 | 0 | monitor_check_panes_windows(ms); |
529 | 0 | if (have_all_panes) |
530 | 0 | monitor_check_all_panes(ms); |
531 | 0 | if (have_all_windows) |
532 | 0 | monitor_check_all_windows(ms); |
533 | 0 | } |
534 | | |
535 | | /* Create a monitor set. */ |
536 | | static struct monitor_set * |
537 | | monitor_create(monitor_cb cb, void *data) |
538 | 0 | { |
539 | 0 | struct monitor_set *ms; |
540 | |
|
541 | 0 | ms = xcalloc(1, sizeof *ms); |
542 | 0 | ms->cb = cb; |
543 | 0 | ms->data = data; |
544 | 0 | RB_INIT(&ms->items); |
545 | 0 | return (ms); |
546 | 0 | } |
547 | | |
548 | | /* Create a client monitor set. */ |
549 | | struct monitor_set * |
550 | | monitor_create_client(struct client *c, monitor_cb cb, void *data) |
551 | 0 | { |
552 | 0 | struct monitor_set *ms; |
553 | |
|
554 | 0 | ms = monitor_create(cb, data); |
555 | 0 | ms->client = c; |
556 | 0 | return (ms); |
557 | 0 | } |
558 | | |
559 | | /* Create a monitor set for a session. */ |
560 | | struct monitor_set * |
561 | | monitor_create_session(struct session *s, monitor_cb cb, void *data) |
562 | 0 | { |
563 | 0 | struct monitor_set *ms; |
564 | |
|
565 | 0 | ms = monitor_create(cb, data); |
566 | 0 | ms->session = s; |
567 | 0 | if (s != NULL) |
568 | 0 | session_add_ref(s, __func__); |
569 | 0 | return (ms); |
570 | 0 | } |
571 | | |
572 | | /* Destroy a monitor set. */ |
573 | | void |
574 | | monitor_destroy(struct monitor_set *ms) |
575 | 0 | { |
576 | 0 | struct monitor_item *me, *me1; |
577 | |
|
578 | 0 | if (ms != NULL) { |
579 | 0 | if (evtimer_initialized(&ms->timer)) |
580 | 0 | evtimer_del(&ms->timer); |
581 | 0 | RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) |
582 | 0 | monitor_free_item(ms, me); |
583 | 0 | if (ms->session != NULL) |
584 | 0 | session_remove_ref(ms->session, __func__); |
585 | 0 | free(ms); |
586 | 0 | } |
587 | 0 | } |
588 | | |
589 | | /* Parse a subscription. */ |
590 | | int |
591 | | monitor_parse(const char *value, char **name, enum monitor_type *type, int *id, |
592 | | char **format) |
593 | 0 | { |
594 | 0 | char *copy, *what, *split; |
595 | |
|
596 | 0 | copy = xstrdup(value); |
597 | 0 | *id = -1; |
598 | |
|
599 | 0 | what = strchr(copy, ':'); |
600 | 0 | if (what == NULL) |
601 | 0 | goto fail; |
602 | 0 | *what++ = '\0'; |
603 | |
|
604 | 0 | split = strchr(what, ':'); |
605 | 0 | if (split == NULL) |
606 | 0 | goto fail; |
607 | 0 | *split++ = '\0'; |
608 | |
|
609 | 0 | if (strcmp(what, "%*") == 0) |
610 | 0 | *type = MONITOR_ALL_PANES; |
611 | 0 | else if (sscanf(what, "%%%d", id) == 1 && *id >= 0) |
612 | 0 | *type = MONITOR_PANE; |
613 | 0 | else if (strcmp(what, "@*") == 0) |
614 | 0 | *type = MONITOR_ALL_WINDOWS; |
615 | 0 | else if (sscanf(what, "@%d", id) == 1 && *id >= 0) |
616 | 0 | *type = MONITOR_WINDOW; |
617 | 0 | else |
618 | 0 | *type = MONITOR_SESSION; |
619 | 0 | *name = xstrdup(copy); |
620 | 0 | *format = xstrdup(split); |
621 | |
|
622 | 0 | free(copy); |
623 | 0 | return (0); |
624 | | |
625 | 0 | fail: |
626 | 0 | free(copy); |
627 | 0 | return (-1); |
628 | 0 | } |
629 | | |
630 | | /* Add a subscription. */ |
631 | | void |
632 | | monitor_add(struct monitor_set *ms, const char *name, enum monitor_type type, |
633 | | int id, const char *format, int flags) |
634 | 0 | { |
635 | 0 | struct monitor_item *me, find = { .name = (char *)name }; |
636 | 0 | struct timeval tv = { .tv_sec = 1 }; |
637 | |
|
638 | 0 | if ((me = RB_FIND(monitor_items, &ms->items, &find)) != NULL) |
639 | 0 | monitor_free_item(ms, me); |
640 | |
|
641 | 0 | me = xcalloc(1, sizeof *me); |
642 | 0 | me->name = xstrdup(name); |
643 | 0 | me->format = xstrdup(format); |
644 | 0 | me->type = type; |
645 | 0 | me->id = id; |
646 | 0 | me->flags = flags; |
647 | 0 | RB_INIT(&me->panes); |
648 | 0 | RB_INIT(&me->windows); |
649 | 0 | RB_INSERT(monitor_items, &ms->items, me); |
650 | |
|
651 | 0 | if (!evtimer_initialized(&ms->timer)) |
652 | 0 | evtimer_set(&ms->timer, monitor_timer, ms); |
653 | 0 | if (!evtimer_pending(&ms->timer, NULL)) |
654 | 0 | evtimer_add(&ms->timer, &tv); |
655 | 0 | } |
656 | | |
657 | | /* Remove a subscription. */ |
658 | | void |
659 | | monitor_remove(struct monitor_set *ms, const char *name) |
660 | 0 | { |
661 | 0 | struct monitor_item *me, find = { .name = (char *)name }; |
662 | |
|
663 | 0 | if ((me = RB_FIND(monitor_items, &ms->items, &find)) != NULL) |
664 | 0 | monitor_free_item(ms, me); |
665 | 0 | if (RB_EMPTY(&ms->items) && evtimer_initialized(&ms->timer)) |
666 | 0 | evtimer_del(&ms->timer); |
667 | 0 | } |
668 | | |
669 | | /* Get subscription firing count. */ |
670 | | u_int |
671 | | monitor_get_fire_count(struct monitor_set *ms, const char *name) |
672 | 0 | { |
673 | 0 | struct monitor_item *me, find = { .name = (char *)name }; |
674 | |
|
675 | 0 | if ((me = RB_FIND(monitor_items, &ms->items, &find)) == NULL) |
676 | 0 | return (0); |
677 | 0 | return (me->fire_count); |
678 | 0 | } |
679 | | |
680 | | /* Get subscription firing time. */ |
681 | | time_t |
682 | | monitor_get_fire_time(struct monitor_set *ms, const char *name) |
683 | 0 | { |
684 | 0 | struct monitor_item *me, find = { .name = (char *)name }; |
685 | |
|
686 | 0 | if ((me = RB_FIND(monitor_items, &ms->items, &find)) == NULL) |
687 | 0 | return (0); |
688 | 0 | return (me->fire_time); |
689 | 0 | } |