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