Coverage Report

Created: 2026-06-12 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/control.c
Line
Count
Source
1
/* $OpenBSD$ */
2
3
/*
4
 * Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 * Copyright (c) 2012 George Nachman <tmux@georgester.com>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 */
19
20
#include <sys/types.h>
21
22
#include <stdlib.h>
23
#include <string.h>
24
#include <time.h>
25
#include <unistd.h>
26
27
#include "tmux.h"
28
29
/*
30
 * Block of data to output. Each client has one "all" queue of blocks and
31
 * another queue for each pane (in struct client_offset). %output blocks are
32
 * added to both queues and other output lines (notifications) added only to
33
 * the client queue.
34
 *
35
 * When a client becomes writeable, data from blocks on the pane queue are sent
36
 * up to the maximum size (CLIENT_BUFFER_HIGH). If a block is entirely written,
37
 * it is removed from both pane and client queues and if this means non-%output
38
 * blocks are now at the head of the client queue, they are written.
39
 *
40
 * This means a %output block holds up any subsequent non-%output blocks until
41
 * it is written which enforces ordering even if the client cannot accept the
42
 * entire block in one go.
43
 */
44
struct control_block {
45
  size_t         size;
46
  char        *line;
47
  uint64_t       t;
48
49
  TAILQ_ENTRY(control_block)   entry;
50
  TAILQ_ENTRY(control_block)   all_entry;
51
};
52
53
/* Control client pane. */
54
struct control_pane {
55
  u_int        pane;
56
57
  /*
58
   * Offsets into the pane data. The first (offset) is the data we have
59
   * written; the second (queued) the data we have queued (pointed to by
60
   * a block).
61
   */
62
  struct window_pane_offset  offset;
63
  struct window_pane_offset  queued;
64
65
  int        flags;
66
0
#define CONTROL_PANE_OFF 0x1
67
0
#define CONTROL_PANE_PAUSED 0x2
68
69
  int        pending_flag;
70
  TAILQ_ENTRY(control_pane)  pending_entry;
71
72
  TAILQ_HEAD(, control_block)  blocks;
73
74
  RB_ENTRY(control_pane)     entry;
75
};
76
RB_HEAD(control_panes, control_pane);
77
78
/* Subscription pane. */
79
struct control_sub_pane {
80
  u_int        pane;
81
  u_int        idx;
82
  char        *last;
83
84
  RB_ENTRY(control_sub_pane)   entry;
85
};
86
RB_HEAD(control_sub_panes, control_sub_pane);
87
88
/* Subscription window. */
89
struct control_sub_window {
90
  u_int        window;
91
  u_int        idx;
92
  char        *last;
93
94
  RB_ENTRY(control_sub_window)   entry;
95
};
96
RB_HEAD(control_sub_windows, control_sub_window);
97
98
/* Control client subscription. */
99
struct control_sub {
100
  char        *name;
101
  char        *format;
102
103
  enum control_sub_type    type;
104
  u_int        id;
105
106
  char        *last;
107
  struct control_sub_panes   panes;
108
  struct control_sub_windows   windows;
109
110
  RB_ENTRY(control_sub)    entry;
111
};
112
RB_HEAD(control_subs, control_sub);
113
114
/* Control client state. */
115
struct control_state {
116
  struct control_panes     panes;
117
118
  TAILQ_HEAD(, control_pane)   pending_list;
119
  u_int        pending_count;
120
121
  TAILQ_HEAD(, control_block)  all_blocks;
122
123
  struct bufferevent    *read_event;
124
  struct bufferevent    *write_event;
125
126
  struct control_subs    subs;
127
  struct event       subs_timer;
128
};
129
130
/* Low and high watermarks. */
131
0
#define CONTROL_BUFFER_LOW 512
132
0
#define CONTROL_BUFFER_HIGH 8192
133
134
/* Minimum to write to each client. */
135
0
#define CONTROL_WRITE_MINIMUM 32
136
137
/* Maximum age for clients that are not using pause mode. */
138
0
#define CONTROL_MAXIMUM_AGE 300000
139
140
/* Flags to ignore client. */
141
#define CONTROL_IGNORE_FLAGS \
142
0
  (CLIENT_CONTROL_NOOUTPUT| \
143
0
   CLIENT_UNATTACHEDFLAGS)
144
145
/* Compare client panes. */
146
static int
147
control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
148
0
{
149
0
  if (cp1->pane < cp2->pane)
150
0
    return (-1);
151
0
  if (cp1->pane > cp2->pane)
152
0
    return (1);
153
0
  return (0);
154
0
}
155
0
RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
Unexecuted instantiation: control.c:control_panes_RB_MINMAX
Unexecuted instantiation: control.c:control_panes_RB_REMOVE
Unexecuted instantiation: control.c:control_panes_RB_REMOVE_COLOR
Unexecuted instantiation: control.c:control_panes_RB_FIND
Unexecuted instantiation: control.c:control_panes_RB_INSERT
156
0
157
0
/* Compare client subs. */
158
0
static int
159
0
control_sub_cmp(struct control_sub *csub1, struct control_sub *csub2)
160
0
{
161
0
  return (strcmp(csub1->name, csub2->name));
162
0
}
163
0
RB_GENERATE_STATIC(control_subs, control_sub, entry, control_sub_cmp);
Unexecuted instantiation: control.c:control_subs_RB_MINMAX
Unexecuted instantiation: control.c:control_subs_RB_REMOVE
Unexecuted instantiation: control.c:control_subs_RB_REMOVE_COLOR
Unexecuted instantiation: control.c:control_subs_RB_FIND
Unexecuted instantiation: control.c:control_subs_RB_INSERT
164
0
165
0
/* Compare client subscription panes. */
166
0
static int
167
0
control_sub_pane_cmp(struct control_sub_pane *csp1,
168
0
    struct control_sub_pane *csp2)
169
0
{
170
0
  if (csp1->pane < csp2->pane)
171
0
    return (-1);
172
0
  if (csp1->pane > csp2->pane)
173
0
    return (1);
174
0
  if (csp1->idx < csp2->idx)
175
0
    return (-1);
176
0
  if (csp1->idx > csp2->idx)
177
0
    return (1);
178
0
  return (0);
179
0
}
180
0
RB_GENERATE_STATIC(control_sub_panes, control_sub_pane, entry,
Unexecuted instantiation: control.c:control_sub_panes_RB_MINMAX
Unexecuted instantiation: control.c:control_sub_panes_RB_REMOVE
Unexecuted instantiation: control.c:control_sub_panes_RB_REMOVE_COLOR
Unexecuted instantiation: control.c:control_sub_panes_RB_FIND
Unexecuted instantiation: control.c:control_sub_panes_RB_INSERT
181
0
    control_sub_pane_cmp);
182
0
183
0
/* Compare client subscription windows. */
184
0
static int
185
0
control_sub_window_cmp(struct control_sub_window *csw1,
186
0
    struct control_sub_window *csw2)
187
0
{
188
0
  if (csw1->window < csw2->window)
189
0
    return (-1);
190
0
  if (csw1->window > csw2->window)
191
0
    return (1);
192
0
  if (csw1->idx < csw2->idx)
193
0
    return (-1);
194
0
  if (csw1->idx > csw2->idx)
195
0
    return (1);
196
0
  return (0);
197
0
}
198
0
RB_GENERATE_STATIC(control_sub_windows, control_sub_window, entry,
Unexecuted instantiation: control.c:control_sub_windows_RB_MINMAX
Unexecuted instantiation: control.c:control_sub_windows_RB_REMOVE
Unexecuted instantiation: control.c:control_sub_windows_RB_REMOVE_COLOR
Unexecuted instantiation: control.c:control_sub_windows_RB_FIND
Unexecuted instantiation: control.c:control_sub_windows_RB_INSERT
199
0
    control_sub_window_cmp);
200
0
201
0
/* Free a subscription. */
202
0
static void
203
0
control_free_sub(struct control_state *cs, struct control_sub *csub)
204
0
{
205
0
  struct control_sub_pane   *csp, *csp1;
206
0
  struct control_sub_window *csw, *csw1;
207
208
0
  RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) {
209
0
    RB_REMOVE(control_sub_panes, &csub->panes, csp);
210
0
    free(csp->last);
211
0
    free(csp);
212
0
  }
213
0
  RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) {
214
0
    RB_REMOVE(control_sub_windows, &csub->windows, csw);
215
0
    free(csw->last);
216
0
    free(csw);
217
0
  }
218
0
  free(csub->last);
219
220
0
  RB_REMOVE(control_subs, &cs->subs, csub);
221
0
  free(csub->name);
222
0
  free(csub->format);
223
0
  free(csub);
224
0
}
225
226
/* Free a block. */
227
static void
228
control_free_block(struct control_state *cs, struct control_block *cb)
229
0
{
230
0
  free(cb->line);
231
0
  TAILQ_REMOVE(&cs->all_blocks, cb, all_entry);
232
0
  free(cb);
233
0
}
234
235
/* Get pane offsets for this client. */
236
static struct control_pane *
237
control_get_pane(struct client *c, struct window_pane *wp)
238
0
{
239
0
  struct control_state  *cs = c->control_state;
240
0
  struct control_pane  cp = { .pane = wp->id };
241
242
0
  return (RB_FIND(control_panes, &cs->panes, &cp));
243
0
}
244
245
/* Add pane offsets for this client. */
246
static struct control_pane *
247
control_add_pane(struct client *c, struct window_pane *wp)
248
0
{
249
0
  struct control_state  *cs = c->control_state;
250
0
  struct control_pane *cp;
251
252
0
  cp = control_get_pane(c, wp);
253
0
  if (cp != NULL)
254
0
    return (cp);
255
256
0
  cp = xcalloc(1, sizeof *cp);
257
0
  cp->pane = wp->id;
258
0
  RB_INSERT(control_panes, &cs->panes, cp);
259
260
0
  memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
261
0
  memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
262
0
  TAILQ_INIT(&cp->blocks);
263
264
0
  return (cp);
265
0
}
266
267
/* Discard output for a pane. */
268
static void
269
control_discard_pane(struct client *c, struct control_pane *cp)
270
0
{
271
0
  struct control_state  *cs = c->control_state;
272
0
  struct control_block  *cb, *cb1;
273
274
0
  TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) {
275
0
    TAILQ_REMOVE(&cp->blocks, cb, entry);
276
0
    control_free_block(cs, cb);
277
0
  }
278
0
}
279
280
/* Get actual pane for this client. */
281
static struct window_pane *
282
control_window_pane(struct client *c, u_int pane)
283
0
{
284
0
  struct window_pane  *wp;
285
286
0
  if (c->session == NULL)
287
0
    return (NULL);
288
0
  if ((wp = window_pane_find_by_id(pane)) == NULL)
289
0
    return (NULL);
290
0
  if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
291
0
    return (NULL);
292
0
  return (wp);
293
0
}
294
295
/* Reset control offsets. */
296
void
297
control_reset_offsets(struct client *c)
298
0
{
299
0
  struct control_state  *cs = c->control_state;
300
0
  struct control_pane *cp, *cp1;
301
302
0
  RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) {
303
0
    control_discard_pane(c, cp);
304
0
    RB_REMOVE(control_panes, &cs->panes, cp);
305
0
    free(cp);
306
0
  }
307
308
0
  TAILQ_INIT(&cs->pending_list);
309
0
  cs->pending_count = 0;
310
0
}
311
312
/* Get offsets for client. */
313
struct window_pane_offset *
314
control_pane_offset(struct client *c, struct window_pane *wp, int *off)
315
0
{
316
0
  struct control_state  *cs = c->control_state;
317
0
  struct control_pane *cp;
318
319
0
  if (c->flags & CLIENT_CONTROL_NOOUTPUT) {
320
0
    *off = 0;
321
0
    return (NULL);
322
0
  }
323
324
0
  cp = control_get_pane(c, wp);
325
0
  if (cp == NULL || (cp->flags & CONTROL_PANE_PAUSED)) {
326
0
    *off = 0;
327
0
    return (NULL);
328
0
  }
329
0
  if (cp->flags & CONTROL_PANE_OFF) {
330
0
    *off = 1;
331
0
    return (NULL);
332
0
  }
333
0
  *off = (EVBUFFER_LENGTH(cs->write_event->output) >= CONTROL_BUFFER_LOW);
334
0
  return (&cp->offset);
335
0
}
336
337
/* Set pane as on. */
338
void
339
control_set_pane_on(struct client *c, struct window_pane *wp)
340
0
{
341
0
  struct control_pane *cp;
342
343
0
  cp = control_get_pane(c, wp);
344
0
  if (cp != NULL && (cp->flags & CONTROL_PANE_OFF)) {
345
0
    cp->flags &= ~CONTROL_PANE_OFF;
346
0
    memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
347
0
    memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
348
0
  }
349
0
}
350
351
/* Set pane as off. */
352
void
353
control_set_pane_off(struct client *c, struct window_pane *wp)
354
0
{
355
0
  struct control_pane *cp;
356
357
0
  cp = control_add_pane(c, wp);
358
0
  control_discard_pane(c, cp);
359
0
  memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
360
0
  memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
361
0
  cp->flags |= CONTROL_PANE_OFF;
362
0
}
363
364
/* Continue a paused pane. */
365
void
366
control_continue_pane(struct client *c, struct window_pane *wp)
367
0
{
368
0
  struct control_pane *cp;
369
370
0
  cp = control_get_pane(c, wp);
371
0
  if (cp != NULL && (cp->flags & CONTROL_PANE_PAUSED)) {
372
0
    cp->flags &= ~CONTROL_PANE_PAUSED;
373
0
    memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
374
0
    memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
375
0
    control_write(c, "%%continue %%%u", wp->id);
376
0
  }
377
0
}
378
379
/* Pause a pane. */
380
void
381
control_pause_pane(struct client *c, struct window_pane *wp)
382
0
{
383
0
  struct control_pane *cp;
384
385
0
  cp = control_add_pane(c, wp);
386
0
  if (~cp->flags & CONTROL_PANE_PAUSED) {
387
0
    cp->flags |= CONTROL_PANE_PAUSED;
388
0
    control_discard_pane(c, cp);
389
0
    control_write(c, "%%pause %%%u", wp->id);
390
0
  }
391
0
}
392
393
/* Write a line. */
394
static void printflike(2, 0)
395
control_vwrite(struct client *c, const char *fmt, va_list ap)
396
0
{
397
0
  struct control_state  *cs = c->control_state;
398
0
  char      *s;
399
400
0
  xvasprintf(&s, fmt, ap);
401
0
  log_debug("%s: %s: writing line: %s", __func__, c->name, s);
402
403
0
  bufferevent_write(cs->write_event, s, strlen(s));
404
0
  bufferevent_write(cs->write_event, "\n", 1);
405
406
0
  bufferevent_enable(cs->write_event, EV_WRITE);
407
0
  free(s);
408
0
}
409
410
/* Write a line. */
411
void
412
control_write(struct client *c, const char *fmt, ...)
413
0
{
414
0
  struct control_state  *cs = c->control_state;
415
0
  struct control_block  *cb;
416
0
  va_list      ap;
417
418
0
  va_start(ap, fmt);
419
420
0
  if (TAILQ_EMPTY(&cs->all_blocks)) {
421
0
    control_vwrite(c, fmt, ap);
422
0
    va_end(ap);
423
0
    return;
424
0
  }
425
426
0
  cb = xcalloc(1, sizeof *cb);
427
0
  xvasprintf(&cb->line, fmt, ap);
428
0
  TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
429
0
  cb->t = get_timer();
430
431
0
  log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line);
432
0
  bufferevent_enable(cs->write_event, EV_WRITE);
433
434
0
  va_end(ap);
435
0
}
436
437
/* Check age for this pane. */
438
static int
439
control_check_age(struct client *c, struct window_pane *wp,
440
    struct control_pane *cp)
441
0
{
442
0
  struct control_block  *cb;
443
0
  uint64_t     t, age;
444
445
0
  cb = TAILQ_FIRST(&cp->blocks);
446
0
  if (cb == NULL)
447
0
    return (0);
448
0
  t = get_timer();
449
0
  if (cb->t >= t)
450
0
    return (0);
451
452
0
  age = t - cb->t;
453
0
  log_debug("%s: %s: %%%u is %llu behind", __func__, c->name, wp->id,
454
0
      (unsigned long long)age);
455
456
0
  if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
457
0
    if (age < c->pause_age)
458
0
      return (0);
459
0
    cp->flags |= CONTROL_PANE_PAUSED;
460
0
    control_discard_pane(c, cp);
461
0
    control_write(c, "%%pause %%%u", wp->id);
462
0
  } else {
463
0
    if (age < CONTROL_MAXIMUM_AGE)
464
0
      return (0);
465
0
    c->exit_message = xstrdup("too far behind");
466
0
    c->flags |= CLIENT_EXIT;
467
0
    control_discard(c);
468
0
  }
469
0
  return (1);
470
0
}
471
472
/* Write output from a pane. */
473
void
474
control_write_output(struct client *c, struct window_pane *wp)
475
0
{
476
0
  struct control_state  *cs = c->control_state;
477
0
  struct control_pane *cp;
478
0
  struct control_block  *cb;
479
0
  size_t       new_size;
480
481
0
  if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
482
0
    return;
483
484
0
  if (c->flags & CONTROL_IGNORE_FLAGS) {
485
0
    cp = control_get_pane(c, wp);
486
0
    if (cp != NULL)
487
0
      goto ignore;
488
0
    return;
489
0
  }
490
0
  cp = control_add_pane(c, wp);
491
0
  if (cp->flags & (CONTROL_PANE_OFF|CONTROL_PANE_PAUSED))
492
0
    goto ignore;
493
0
  if (control_check_age(c, wp, cp))
494
0
    return;
495
496
0
  window_pane_get_new_data(wp, &cp->queued, &new_size);
497
0
  if (new_size == 0)
498
0
    return;
499
0
  window_pane_update_used_data(wp, &cp->queued, new_size);
500
501
0
  cb = xcalloc(1, sizeof *cb);
502
0
  cb->size = new_size;
503
0
  TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
504
0
  cb->t = get_timer();
505
506
0
  TAILQ_INSERT_TAIL(&cp->blocks, cb, entry);
507
0
  log_debug("%s: %s: new output block of %zu for %%%u", __func__, c->name,
508
0
      cb->size, wp->id);
509
510
0
  if (!cp->pending_flag) {
511
0
    log_debug("%s: %s: %%%u now pending", __func__, c->name,
512
0
        wp->id);
513
0
    TAILQ_INSERT_TAIL(&cs->pending_list, cp, pending_entry);
514
0
    cp->pending_flag = 1;
515
0
    cs->pending_count++;
516
0
  }
517
0
  bufferevent_enable(cs->write_event, EV_WRITE);
518
0
  return;
519
520
0
ignore:
521
0
  log_debug("%s: %s: ignoring pane %%%u", __func__, c->name, wp->id);
522
0
  window_pane_update_used_data(wp, &cp->offset, SIZE_MAX);
523
0
  window_pane_update_used_data(wp, &cp->queued, SIZE_MAX);
524
0
}
525
526
/* Control client error callback. */
527
static enum cmd_retval
528
control_error(struct cmdq_item *item, void *data)
529
0
{
530
0
  struct client *c = cmdq_get_client(item);
531
0
  char    *error = data;
532
533
0
  cmdq_guard(item, "begin", 1);
534
0
  control_write(c, "parse error: %s", error);
535
0
  cmdq_guard(item, "error", 1);
536
537
0
  free(error);
538
0
  return (CMD_RETURN_NORMAL);
539
0
}
540
541
/* Control client error callback. */
542
static void
543
control_error_callback(__unused struct bufferevent *bufev,
544
    __unused short what, void *data)
545
0
{
546
0
  struct client *c = data;
547
548
0
  c->flags |= CLIENT_EXIT;
549
0
}
550
551
/* Control client input callback. Read lines and fire commands. */
552
static void
553
control_read_callback(__unused struct bufferevent *bufev, void *data)
554
0
{
555
0
  struct client   *c = data;
556
0
  struct control_state  *cs = c->control_state;
557
0
  struct evbuffer   *buffer = cs->read_event->input;
558
0
  char      *line, *error;
559
0
  struct cmdq_state *state;
560
0
  enum cmd_parse_status  status;
561
562
0
  for (;;) {
563
0
    line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
564
0
    if (line == NULL)
565
0
      break;
566
0
    log_debug("%s: %s: %s", __func__, c->name, line);
567
0
    if (*line == '\0') { /* empty line detach */
568
0
      free(line);
569
0
      c->flags |= CLIENT_EXIT;
570
0
      break;
571
0
    }
572
573
0
    state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL);
574
0
    status = cmd_parse_and_append(line, NULL, c, state, &error);
575
0
    if (status == CMD_PARSE_ERROR)
576
0
      cmdq_append(c, cmdq_get_callback(control_error, error));
577
0
    cmdq_free_state(state);
578
579
0
    free(line);
580
0
  }
581
0
}
582
583
/* Does this control client have outstanding data to write? */
584
int
585
control_all_done(struct client *c)
586
0
{
587
0
  struct control_state  *cs = c->control_state;
588
589
0
  if (!TAILQ_EMPTY(&cs->all_blocks))
590
0
    return (0);
591
0
  return (EVBUFFER_LENGTH(cs->write_event->output) == 0);
592
0
}
593
594
/* Flush all blocks until output. */
595
static void
596
control_flush_all_blocks(struct client *c)
597
0
{
598
0
  struct control_state  *cs = c->control_state;
599
0
  struct control_block  *cb, *cb1;
600
601
0
  TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) {
602
0
    if (cb->size != 0)
603
0
      break;
604
0
    log_debug("%s: %s: flushing line: %s", __func__, c->name,
605
0
        cb->line);
606
607
0
    bufferevent_write(cs->write_event, cb->line, strlen(cb->line));
608
0
    bufferevent_write(cs->write_event, "\n", 1);
609
0
    control_free_block(cs, cb);
610
0
  }
611
0
}
612
613
/* Append data to buffer. */
614
static struct evbuffer *
615
control_append_data(struct client *c, struct control_pane *cp, uint64_t age,
616
    struct evbuffer *message, struct window_pane *wp, size_t size)
617
0
{
618
0
  u_char  *new_data;
619
0
  size_t   new_size, start;
620
0
  u_int  i;
621
622
0
  if (message == NULL) {
623
0
    message = evbuffer_new();
624
0
    if (message == NULL)
625
0
      fatalx("out of memory");
626
0
    if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
627
0
      evbuffer_add_printf(message,
628
0
          "%%extended-output %%%u %llu : ", wp->id,
629
0
          (unsigned long long)age);
630
0
    } else
631
0
      evbuffer_add_printf(message, "%%output %%%u ", wp->id);
632
0
  }
633
634
0
  new_data = window_pane_get_new_data(wp, &cp->offset, &new_size);
635
0
  if (new_size < size)
636
0
    fatalx("not enough data: %zu < %zu", new_size, size);
637
0
  for (i = 0; i < size; i++) {
638
0
    if (new_data[i] < ' ' || new_data[i] == '\\') {
639
0
      evbuffer_add_printf(message, "\\%03o", new_data[i]);
640
0
    } else {
641
0
      start = i;
642
0
      while (i + 1 < size &&
643
0
          new_data[i + 1] >= ' ' &&
644
0
          new_data[i + 1] != '\\')
645
0
        i++;
646
0
      evbuffer_add(message, new_data + start, i - start + 1);
647
0
    }
648
0
  }
649
0
  window_pane_update_used_data(wp, &cp->offset, size);
650
0
  return (message);
651
0
}
652
653
/* Write buffer. */
654
static void
655
control_write_data(struct client *c, struct evbuffer *message)
656
0
{
657
0
  struct control_state  *cs = c->control_state;
658
659
0
  log_debug("%s: %s: %.*s", __func__, c->name,
660
0
      (int)EVBUFFER_LENGTH(message), EVBUFFER_DATA(message));
661
662
0
  evbuffer_add(message, "\n", 1);
663
0
  bufferevent_write_buffer(cs->write_event, message);
664
0
  evbuffer_free(message);
665
0
}
666
667
/* Write output to client. */
668
static int
669
control_write_pending(struct client *c, struct control_pane *cp, size_t limit)
670
0
{
671
0
  struct control_state  *cs = c->control_state;
672
0
  struct window_pane  *wp = NULL;
673
0
  struct evbuffer   *message = NULL;
674
0
  size_t       used = 0, size;
675
0
  struct control_block  *cb, *cb1;
676
0
  uint64_t     age, t = get_timer();
677
678
0
  wp = control_window_pane(c, cp->pane);
679
0
  if (wp == NULL || wp->fd == -1) {
680
0
    TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) {
681
0
      TAILQ_REMOVE(&cp->blocks, cb, entry);
682
0
      control_free_block(cs, cb);
683
0
    }
684
0
    control_flush_all_blocks(c);
685
0
    return (0);
686
0
  }
687
688
0
  while (used != limit && !TAILQ_EMPTY(&cp->blocks)) {
689
0
    if (control_check_age(c, wp, cp)) {
690
0
      if (message != NULL)
691
0
        evbuffer_free(message);
692
0
      message = NULL;
693
0
      break;
694
0
    }
695
696
0
    cb = TAILQ_FIRST(&cp->blocks);
697
0
    if (cb->t < t)
698
0
      age = t - cb->t;
699
0
    else
700
0
      age = 0;
701
0
    log_debug("%s: %s: output block %zu (age %llu) for %%%u "
702
0
        "(used %zu/%zu)", __func__, c->name, cb->size,
703
0
        (unsigned long long)age, cp->pane, used, limit);
704
705
0
    size = cb->size;
706
0
    if (size > limit - used)
707
0
      size = limit - used;
708
0
    used += size;
709
710
0
    message = control_append_data(c, cp, age, message, wp, size);
711
712
0
    cb->size -= size;
713
0
    if (cb->size == 0) {
714
0
      TAILQ_REMOVE(&cp->blocks, cb, entry);
715
0
      control_free_block(cs, cb);
716
717
0
      cb = TAILQ_FIRST(&cs->all_blocks);
718
0
      if (cb != NULL && cb->size == 0) {
719
0
        if (wp != NULL && message != NULL) {
720
0
          control_write_data(c, message);
721
0
          message = NULL;
722
0
        }
723
0
        control_flush_all_blocks(c);
724
0
      }
725
0
    }
726
0
  }
727
0
  if (message != NULL)
728
0
    control_write_data(c, message);
729
0
  return (!TAILQ_EMPTY(&cp->blocks));
730
0
}
731
732
/* Control client write callback. */
733
static void
734
control_write_callback(__unused struct bufferevent *bufev, void *data)
735
0
{
736
0
  struct client   *c = data;
737
0
  struct control_state  *cs = c->control_state;
738
0
  struct control_pane *cp, *cp1;
739
0
  struct evbuffer   *evb = cs->write_event->output;
740
0
  size_t       space, limit;
741
742
0
  control_flush_all_blocks(c);
743
744
0
  while (EVBUFFER_LENGTH(evb) < CONTROL_BUFFER_HIGH) {
745
0
    if (cs->pending_count == 0)
746
0
      break;
747
0
    space = CONTROL_BUFFER_HIGH - EVBUFFER_LENGTH(evb);
748
0
    log_debug("%s: %s: %zu bytes available, %u panes", __func__,
749
0
        c->name, space, cs->pending_count);
750
751
0
    limit = (space / cs->pending_count / 3); /* 3 bytes for \xxx */
752
0
    if (limit < CONTROL_WRITE_MINIMUM)
753
0
      limit = CONTROL_WRITE_MINIMUM;
754
755
0
    TAILQ_FOREACH_SAFE(cp, &cs->pending_list, pending_entry, cp1) {
756
0
      if (EVBUFFER_LENGTH(evb) >= CONTROL_BUFFER_HIGH)
757
0
        break;
758
0
      if (control_write_pending(c, cp, limit))
759
0
        continue;
760
0
      TAILQ_REMOVE(&cs->pending_list, cp, pending_entry);
761
0
      cp->pending_flag = 0;
762
0
      cs->pending_count--;
763
0
    }
764
0
  }
765
0
  if (EVBUFFER_LENGTH(evb) == 0)
766
0
    bufferevent_disable(cs->write_event, EV_WRITE);
767
0
}
768
769
/* Initialize for control mode. */
770
void
771
control_start(struct client *c)
772
0
{
773
0
  struct control_state  *cs;
774
775
0
  if (c->flags & CLIENT_CONTROLCONTROL) {
776
0
    close(c->out_fd);
777
0
    c->out_fd = -1;
778
0
  } else
779
0
    setblocking(c->out_fd, 0);
780
0
  setblocking(c->fd, 0);
781
782
0
  cs = c->control_state = xcalloc(1, sizeof *cs);
783
0
  RB_INIT(&cs->panes);
784
0
  TAILQ_INIT(&cs->pending_list);
785
0
  TAILQ_INIT(&cs->all_blocks);
786
0
  RB_INIT(&cs->subs);
787
788
0
  cs->read_event = bufferevent_new(c->fd, control_read_callback,
789
0
      control_write_callback, control_error_callback, c);
790
0
  if (cs->read_event == NULL)
791
0
    fatalx("out of memory");
792
793
0
  if (c->flags & CLIENT_CONTROLCONTROL)
794
0
    cs->write_event = cs->read_event;
795
0
  else {
796
0
    cs->write_event = bufferevent_new(c->out_fd, NULL,
797
0
        control_write_callback, control_error_callback, c);
798
0
    if (cs->write_event == NULL)
799
0
      fatalx("out of memory");
800
0
  }
801
0
  bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
802
0
      0);
803
804
0
  if (c->flags & CLIENT_CONTROLCONTROL) {
805
0
    bufferevent_write(cs->write_event, "\033P1000p", 7);
806
0
    bufferevent_enable(cs->write_event, EV_WRITE);
807
0
  }
808
0
}
809
810
/* Control client ready. */
811
void
812
control_ready(struct client *c)
813
0
{
814
0
  bufferevent_enable(c->control_state->read_event, EV_READ);
815
0
}
816
817
/* Discard all output for a client. */
818
void
819
control_discard(struct client *c)
820
0
{
821
0
  struct control_state  *cs = c->control_state;
822
0
  struct control_pane *cp;
823
824
0
  RB_FOREACH(cp, control_panes, &cs->panes)
825
0
    control_discard_pane(c, cp);
826
0
  bufferevent_disable(cs->read_event, EV_READ);
827
0
}
828
829
/* Stop control mode. */
830
void
831
control_stop(struct client *c)
832
0
{
833
0
  struct control_state  *cs = c->control_state;
834
0
  struct control_block  *cb, *cb1;
835
0
  struct control_sub  *csub, *csub1;
836
837
0
  if (cs == NULL)
838
0
    return;
839
840
0
  if (~c->flags & CLIENT_CONTROLCONTROL)
841
0
    bufferevent_free(cs->write_event);
842
0
  bufferevent_free(cs->read_event);
843
844
0
  RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1)
845
0
    control_free_sub(cs, csub);
846
0
  if (evtimer_initialized(&cs->subs_timer))
847
0
    evtimer_del(&cs->subs_timer);
848
849
0
  control_reset_offsets(c);
850
0
  TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
851
0
    control_free_block(cs, cb);
852
853
0
  c->control_state = NULL;
854
0
  free(cs);
855
0
}
856
857
/* Check session subscription. */
858
static void
859
control_check_subs_session(struct client *c, struct control_sub *csub,
860
    struct format_tree *ft)
861
0
{
862
0
  struct session    *s = c->session;
863
0
  char      *value;
864
865
0
  value = format_expand(ft, csub->format);
866
867
0
  if (csub->last != NULL && strcmp(value, csub->last) == 0) {
868
0
    free(value);
869
0
    return;
870
0
  }
871
0
  control_write(c,
872
0
      "%%subscription-changed %s $%u - - - : %s",
873
0
      csub->name, s->id, value);
874
0
  free(csub->last);
875
0
  csub->last = value;
876
0
}
877
878
/* Check pane subscription. */
879
static void
880
control_check_subs_pane(struct client *c, struct control_sub *csub)
881
0
{
882
0
  struct session    *s = c->session;
883
0
  struct window_pane  *wp;
884
0
  struct window   *w;
885
0
  struct winlink    *wl;
886
0
  struct format_tree  *ft;
887
0
  char      *value;
888
0
  struct control_sub_pane *csp, find;
889
890
0
  wp = window_pane_find_by_id(csub->id);
891
0
  if (wp == NULL || wp->fd == -1)
892
0
    return;
893
0
  w = wp->window;
894
895
0
  TAILQ_FOREACH(wl, &w->winlinks, wentry) {
896
0
    if (wl->session != s)
897
0
      continue;
898
899
0
    ft = format_create_defaults(NULL, c, s, wl, wp);
900
0
    value = format_expand(ft, csub->format);
901
0
    format_free(ft);
902
903
0
    find.pane = wp->id;
904
0
    find.idx = wl->idx;
905
906
0
    csp = RB_FIND(control_sub_panes, &csub->panes, &find);
907
0
    if (csp == NULL) {
908
0
      csp = xcalloc(1, sizeof *csp);
909
0
      csp->pane = wp->id;
910
0
      csp->idx = wl->idx;
911
0
      RB_INSERT(control_sub_panes, &csub->panes, csp);
912
0
    }
913
914
0
    if (csp->last != NULL && strcmp(value, csp->last) == 0) {
915
0
      free(value);
916
0
      continue;
917
0
    }
918
0
    control_write(c,
919
0
        "%%subscription-changed %s $%u @%u %u %%%u : %s",
920
0
        csub->name, s->id, w->id, wl->idx, wp->id, value);
921
0
    free(csp->last);
922
0
    csp->last = value;
923
0
  }
924
0
}
925
926
/* Check all-panes subscription for a pane. */
927
static void
928
control_check_subs_all_panes_one(struct client *c, struct control_sub *csub,
929
    struct format_tree *ft, struct winlink *wl, struct window_pane *wp)
930
0
{
931
0
  struct session    *s = c->session;
932
0
  struct window   *w = wl->window;
933
0
  char      *value;
934
0
  struct control_sub_pane *csp, find;
935
936
0
  value = format_expand(ft, csub->format);
937
938
0
  find.pane = wp->id;
939
0
  find.idx = wl->idx;
940
941
0
  csp = RB_FIND(control_sub_panes, &csub->panes, &find);
942
0
  if (csp == NULL) {
943
0
    csp = xcalloc(1, sizeof *csp);
944
0
    csp->pane = wp->id;
945
0
    csp->idx = wl->idx;
946
0
    RB_INSERT(control_sub_panes, &csub->panes, csp);
947
0
  }
948
949
0
  if (csp->last != NULL && strcmp(value, csp->last) == 0) {
950
0
    free(value);
951
0
    return;
952
0
  }
953
0
  control_write(c,
954
0
      "%%subscription-changed %s $%u @%u %u %%%u : %s",
955
0
      csub->name, s->id, w->id, wl->idx, wp->id, value);
956
0
  free(csp->last);
957
0
  csp->last = value;
958
0
}
959
960
/* Check window subscription. */
961
static void
962
control_check_subs_window(struct client *c, struct control_sub *csub)
963
0
{
964
0
  struct session      *s = c->session;
965
0
  struct window     *w;
966
0
  struct winlink      *wl;
967
0
  struct format_tree    *ft;
968
0
  char        *value;
969
0
  struct control_sub_window *csw, find;
970
971
0
  w = window_find_by_id(csub->id);
972
0
  if (w == NULL)
973
0
    return;
974
975
0
  TAILQ_FOREACH(wl, &w->winlinks, wentry) {
976
0
    if (wl->session != s)
977
0
      continue;
978
979
0
    ft = format_create_defaults(NULL, c, s, wl, NULL);
980
0
    value = format_expand(ft, csub->format);
981
0
    format_free(ft);
982
983
0
    find.window = w->id;
984
0
    find.idx = wl->idx;
985
986
0
    csw = RB_FIND(control_sub_windows, &csub->windows, &find);
987
0
    if (csw == NULL) {
988
0
      csw = xcalloc(1, sizeof *csw);
989
0
      csw->window = w->id;
990
0
      csw->idx = wl->idx;
991
0
      RB_INSERT(control_sub_windows, &csub->windows, csw);
992
0
    }
993
994
0
    if (csw->last != NULL && strcmp(value, csw->last) == 0) {
995
0
      free(value);
996
0
      continue;
997
0
    }
998
0
    control_write(c,
999
0
        "%%subscription-changed %s $%u @%u %u - : %s",
1000
0
        csub->name, s->id, w->id, wl->idx, value);
1001
0
    free(csw->last);
1002
0
    csw->last = value;
1003
0
  }
1004
0
}
1005
1006
/* Check all-windows subscription for a window. */
1007
static void
1008
control_check_subs_all_windows_one(struct client *c, struct control_sub *csub,
1009
    struct format_tree *ft, struct winlink *wl)
1010
0
{
1011
0
  struct session      *s = c->session;
1012
0
  struct window     *w = wl->window;
1013
0
  char        *value;
1014
0
  struct control_sub_window *csw, find;
1015
1016
0
  value = format_expand(ft, csub->format);
1017
1018
0
  find.window = w->id;
1019
0
  find.idx = wl->idx;
1020
1021
0
  csw = RB_FIND(control_sub_windows, &csub->windows, &find);
1022
0
  if (csw == NULL) {
1023
0
    csw = xcalloc(1, sizeof *csw);
1024
0
    csw->window = w->id;
1025
0
    csw->idx = wl->idx;
1026
0
    RB_INSERT(control_sub_windows, &csub->windows, csw);
1027
0
  }
1028
1029
0
  if (csw->last != NULL && strcmp(value, csw->last) == 0) {
1030
0
    free(value);
1031
0
    return;
1032
0
  }
1033
0
  control_write(c,
1034
0
      "%%subscription-changed %s $%u @%u %u - : %s",
1035
0
      csub->name, s->id, w->id, wl->idx, value);
1036
0
  free(csw->last);
1037
0
  csw->last = value;
1038
0
}
1039
1040
/* Check subscriptions timer. */
1041
static void
1042
control_check_subs_timer(__unused int fd, __unused short events, void *data)
1043
0
{
1044
0
  struct client   *c = data;
1045
0
  struct control_state  *cs = c->control_state;
1046
0
  struct control_sub  *csub, *csub1;
1047
0
  struct session    *s = c->session;
1048
0
  struct format_tree  *ft;
1049
0
  struct winlink    *wl;
1050
0
  struct window_pane  *wp;
1051
0
  struct timeval     tv = { .tv_sec = 1 };
1052
0
  int      have_session = 0, have_all_panes = 0;
1053
0
  int      have_all_windows = 0;
1054
1055
0
  log_debug("%s: timer fired", __func__);
1056
0
  evtimer_add(&cs->subs_timer, &tv);
1057
1058
0
  if (s == NULL)
1059
0
    return;
1060
1061
  /* Find which subscription types are present. */
1062
0
  RB_FOREACH(csub, control_subs, &cs->subs) {
1063
0
    switch (csub->type) {
1064
0
    case CONTROL_SUB_SESSION:
1065
0
      have_session = 1;
1066
0
      break;
1067
0
    case CONTROL_SUB_ALL_PANES:
1068
0
      have_all_panes = 1;
1069
0
      break;
1070
0
    case CONTROL_SUB_ALL_WINDOWS:
1071
0
      have_all_windows = 1;
1072
0
      break;
1073
0
    default:
1074
0
      break;
1075
0
    }
1076
0
  }
1077
1078
  /* Check session subscriptions. */
1079
0
  if (have_session) {
1080
0
    ft = format_create_defaults(NULL, c, s, NULL, NULL);
1081
0
    RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
1082
0
      if (csub->type == CONTROL_SUB_SESSION)
1083
0
        control_check_subs_session(c, csub, ft);
1084
0
    }
1085
0
    format_free(ft);
1086
0
  }
1087
1088
  /* Check pane and window subscriptions. */
1089
0
  RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
1090
0
    switch (csub->type) {
1091
0
    case CONTROL_SUB_PANE:
1092
0
      control_check_subs_pane(c, csub);
1093
0
      break;
1094
0
    case CONTROL_SUB_WINDOW:
1095
0
      control_check_subs_window(c, csub);
1096
0
      break;
1097
0
    case CONTROL_SUB_SESSION:
1098
0
    case CONTROL_SUB_ALL_PANES:
1099
0
    case CONTROL_SUB_ALL_WINDOWS:
1100
0
      break;
1101
0
    }
1102
0
  }
1103
1104
  /* Check all-panes subscriptions. */
1105
0
  if (have_all_panes) {
1106
0
    RB_FOREACH(wl, winlinks, &s->windows) {
1107
0
      TAILQ_FOREACH(wp, &wl->window->panes, entry) {
1108
0
        ft = format_create_defaults(NULL, c, s, wl, wp);
1109
0
        RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
1110
0
            csub1) {
1111
0
          if (csub->type != CONTROL_SUB_ALL_PANES)
1112
0
            continue;
1113
0
          control_check_subs_all_panes_one(c,
1114
0
              csub, ft, wl, wp);
1115
0
        }
1116
0
        format_free(ft);
1117
0
      }
1118
0
    }
1119
0
  }
1120
1121
  /* Check all-windows subscriptions. */
1122
0
  if (have_all_windows) {
1123
0
    RB_FOREACH(wl, winlinks, &s->windows) {
1124
0
      ft = format_create_defaults(NULL, c, s, wl, NULL);
1125
0
      RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
1126
0
          csub1) {
1127
0
        if (csub->type != CONTROL_SUB_ALL_WINDOWS)
1128
0
          continue;
1129
0
        control_check_subs_all_windows_one(c, csub, ft,
1130
0
            wl);
1131
0
      }
1132
0
      format_free(ft);
1133
0
    }
1134
0
  }
1135
0
}
1136
1137
/* Add a subscription. */
1138
void
1139
control_add_sub(struct client *c, const char *name, enum control_sub_type type,
1140
    int id, const char *format)
1141
0
{
1142
0
  struct control_state  *cs = c->control_state;
1143
0
  struct control_sub  *csub, find;
1144
0
  struct timeval     tv = { .tv_sec = 1 };
1145
1146
0
  find.name = (char *)name;
1147
0
  if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
1148
0
    control_free_sub(cs, csub);
1149
1150
0
  csub = xcalloc(1, sizeof *csub);
1151
0
  csub->name = xstrdup(name);
1152
0
  csub->type = type;
1153
0
  csub->id = id;
1154
0
  csub->format = xstrdup(format);
1155
0
  RB_INSERT(control_subs, &cs->subs, csub);
1156
1157
0
  RB_INIT(&csub->panes);
1158
0
  RB_INIT(&csub->windows);
1159
1160
0
  if (!evtimer_initialized(&cs->subs_timer))
1161
0
    evtimer_set(&cs->subs_timer, control_check_subs_timer, c);
1162
0
  if (!evtimer_pending(&cs->subs_timer, NULL))
1163
0
    evtimer_add(&cs->subs_timer, &tv);
1164
0
}
1165
1166
/* Remove a subscription. */
1167
void
1168
control_remove_sub(struct client *c, const char *name)
1169
0
{
1170
0
  struct control_state  *cs = c->control_state;
1171
0
  struct control_sub  *csub, find;
1172
1173
0
  find.name = (char *)name;
1174
0
  if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
1175
0
    control_free_sub(cs, csub);
1176
0
  if (RB_EMPTY(&cs->subs))
1177
    evtimer_del(&cs->subs_timer);
1178
0
}