Coverage Report

Created: 2026-09-01 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/monitor.c
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
}