Annotation of src/usr.bin/tmux/control.c, Revision 1.46
1.46 ! nicm 1: /* $OpenBSD: control.c,v 1.45 2020/09/18 11:20:59 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);
1.46 ! nicm 668: if (wp == NULL || wp->fd == -1) {
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 "
1.45 nicm 691: "(used %zu/%zu)", __func__, c->name, cb->size,
692: (unsigned long long)age, 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);
1.46 ! nicm 868: if (wp == NULL || wp->fd == -1)
1.44 nicm 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: }