Annotation of src/usr.bin/tmux/control.c, Revision 1.48
1.48 ! nicm 1: /* $OpenBSD: control.c,v 1.47 2021/08/25 07:09:30 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.47 nicm 389: static void printflike(2, 0)
1.35 nicm 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.25 nicm 779:
780: if (c->flags & CLIENT_CONTROLCONTROL)
1.34 nicm 781: cs->write_event = cs->read_event;
782: else {
1.35 nicm 783: cs->write_event = bufferevent_new(c->out_fd, NULL,
784: control_write_callback, control_error_callback, c);
785: }
786: bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
787: 0);
788:
789: if (c->flags & CLIENT_CONTROLCONTROL) {
790: bufferevent_write(cs->write_event, "\033P1000p", 7);
791: bufferevent_enable(cs->write_event, EV_WRITE);
1.34 nicm 792: }
1.48 ! nicm 793: }
! 794:
! 795: /* Control client ready. */
! 796: void
! 797: control_ready(struct client *c)
! 798: {
! 799: bufferevent_enable(c->control_state->read_event, EV_READ);
1.35 nicm 800: }
801:
1.38 nicm 802: /* Discard all output for a client. */
1.35 nicm 803: void
1.38 nicm 804: control_discard(struct client *c)
1.35 nicm 805: {
806: struct control_state *cs = c->control_state;
807: struct control_pane *cp;
1.34 nicm 808:
1.38 nicm 809: RB_FOREACH(cp, control_panes, &cs->panes)
810: control_discard_pane(c, cp);
1.43 nicm 811: bufferevent_disable(cs->read_event, EV_READ);
1.33 nicm 812: }
813:
814: /* Stop control mode. */
815: void
816: control_stop(struct client *c)
817: {
818: struct control_state *cs = c->control_state;
1.35 nicm 819: struct control_block *cb, *cb1;
1.44 nicm 820: struct control_sub *csub, *csub1;
1.34 nicm 821:
822: if (~c->flags & CLIENT_CONTROLCONTROL)
823: bufferevent_free(cs->write_event);
824: bufferevent_free(cs->read_event);
1.33 nicm 825:
1.44 nicm 826: RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1)
827: control_free_sub(cs, csub);
828: if (evtimer_initialized(&cs->subs_timer))
829: evtimer_del(&cs->subs_timer);
830:
1.35 nicm 831: TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
832: control_free_block(cs, cb);
833: control_reset_offsets(c);
834:
1.33 nicm 835: free(cs);
1.44 nicm 836: }
837:
838: /* Check session subscription. */
839: static void
840: control_check_subs_session(struct client *c, struct control_sub *csub)
841: {
842: struct session *s = c->session;
843: struct format_tree *ft;
844: char *value;
845:
846: ft = format_create_defaults(NULL, c, s, NULL, NULL);
847: value = format_expand(ft, csub->format);
848: format_free(ft);
849:
850: if (csub->last != NULL && strcmp(value, csub->last) == 0) {
851: free(value);
852: return;
853: }
854: control_write(c,
855: "%%subscription-changed %s $%u - - - : %s",
856: csub->name, s->id, value);
857: free(csub->last);
858: csub->last = value;
859: }
860:
861: /* Check pane subscription. */
862: static void
863: control_check_subs_pane(struct client *c, struct control_sub *csub)
864: {
865: struct session *s = c->session;
866: struct window_pane *wp;
867: struct window *w;
868: struct winlink *wl;
869: struct format_tree *ft;
870: char *value;
871: struct control_sub_pane *csp, find;
872:
873: wp = window_pane_find_by_id(csub->id);
1.46 nicm 874: if (wp == NULL || wp->fd == -1)
1.44 nicm 875: return;
876: w = wp->window;
877:
878: TAILQ_FOREACH(wl, &w->winlinks, wentry) {
879: if (wl->session != s)
880: continue;
881:
882: ft = format_create_defaults(NULL, c, s, wl, wp);
883: value = format_expand(ft, csub->format);
884: format_free(ft);
885:
886: find.pane = wp->id;
887: find.idx = wl->idx;
888:
889: csp = RB_FIND(control_sub_panes, &csub->panes, &find);
890: if (csp == NULL) {
891: csp = xcalloc(1, sizeof *csp);
892: csp->pane = wp->id;
893: csp->idx = wl->idx;
894: RB_INSERT(control_sub_panes, &csub->panes, csp);
895: }
896:
897: if (csp->last != NULL && strcmp(value, csp->last) == 0) {
898: free(value);
899: continue;
900: }
901: control_write(c,
902: "%%subscription-changed %s $%u @%u %u %%%u : %s",
903: csub->name, s->id, w->id, wl->idx, wp->id, value);
904: free(csp->last);
905: csp->last = value;
906: }
907: }
908:
909: /* Check all panes subscription. */
910: static void
911: control_check_subs_all_panes(struct client *c, struct control_sub *csub)
912: {
913: struct session *s = c->session;
914: struct window_pane *wp;
915: struct window *w;
916: struct winlink *wl;
917: struct format_tree *ft;
918: char *value;
919: struct control_sub_pane *csp, find;
920:
921: RB_FOREACH(wl, winlinks, &s->windows) {
922: w = wl->window;
923: TAILQ_FOREACH(wp, &w->panes, entry) {
924: ft = format_create_defaults(NULL, c, s, wl, wp);
925: value = format_expand(ft, csub->format);
926: format_free(ft);
927:
928: find.pane = wp->id;
929: find.idx = wl->idx;
930:
931: csp = RB_FIND(control_sub_panes, &csub->panes, &find);
932: if (csp == NULL) {
933: csp = xcalloc(1, sizeof *csp);
934: csp->pane = wp->id;
935: csp->idx = wl->idx;
936: RB_INSERT(control_sub_panes, &csub->panes, csp);
937: }
938:
939: if (csp->last != NULL &&
940: strcmp(value, csp->last) == 0) {
941: free(value);
942: continue;
943: }
944: control_write(c,
945: "%%subscription-changed %s $%u @%u %u %%%u : %s",
946: csub->name, s->id, w->id, wl->idx, wp->id, value);
947: free(csp->last);
948: csp->last = value;
949: }
950: }
951: }
952:
953: /* Check window subscription. */
954: static void
955: control_check_subs_window(struct client *c, struct control_sub *csub)
956: {
957: struct session *s = c->session;
958: struct window *w;
959: struct winlink *wl;
960: struct format_tree *ft;
961: char *value;
962: struct control_sub_window *csw, find;
963:
964: w = window_find_by_id(csub->id);
965: if (w == NULL)
966: return;
967:
968: TAILQ_FOREACH(wl, &w->winlinks, wentry) {
969: if (wl->session != s)
970: continue;
971:
972: ft = format_create_defaults(NULL, c, s, wl, NULL);
973: value = format_expand(ft, csub->format);
974: format_free(ft);
975:
976: find.window = w->id;
977: find.idx = wl->idx;
978:
979: csw = RB_FIND(control_sub_windows, &csub->windows, &find);
980: if (csw == NULL) {
981: csw = xcalloc(1, sizeof *csw);
982: csw->window = w->id;
983: csw->idx = wl->idx;
984: RB_INSERT(control_sub_windows, &csub->windows, csw);
985: }
986:
987: if (csw->last != NULL && strcmp(value, csw->last) == 0) {
988: free(value);
989: continue;
990: }
991: control_write(c,
992: "%%subscription-changed %s $%u @%u %u - : %s",
993: csub->name, s->id, w->id, wl->idx, value);
994: free(csw->last);
995: csw->last = value;
996: }
997: }
998:
999: /* Check all windows subscription. */
1000: static void
1001: control_check_subs_all_windows(struct client *c, struct control_sub *csub)
1002: {
1003: struct session *s = c->session;
1004: struct window *w;
1005: struct winlink *wl;
1006: struct format_tree *ft;
1007: char *value;
1008: struct control_sub_window *csw, find;
1009:
1010: RB_FOREACH(wl, winlinks, &s->windows) {
1011: w = wl->window;
1012:
1013: ft = format_create_defaults(NULL, c, s, wl, NULL);
1014: value = format_expand(ft, csub->format);
1015: format_free(ft);
1016:
1017: find.window = w->id;
1018: find.idx = wl->idx;
1019:
1020: csw = RB_FIND(control_sub_windows, &csub->windows, &find);
1021: if (csw == NULL) {
1022: csw = xcalloc(1, sizeof *csw);
1023: csw->window = w->id;
1024: csw->idx = wl->idx;
1025: RB_INSERT(control_sub_windows, &csub->windows, csw);
1026: }
1027:
1028: if (csw->last != NULL && strcmp(value, csw->last) == 0) {
1029: free(value);
1030: continue;
1031: }
1032: control_write(c,
1033: "%%subscription-changed %s $%u @%u %u - : %s",
1034: csub->name, s->id, w->id, wl->idx, value);
1035: free(csw->last);
1036: csw->last = value;
1037: }
1038: }
1039:
1040: /* Check subscriptions timer. */
1041: static void
1042: control_check_subs_timer(__unused int fd, __unused short events, void *data)
1043: {
1044: struct client *c = data;
1045: struct control_state *cs = c->control_state;
1046: struct control_sub *csub, *csub1;
1047: struct timeval tv = { .tv_sec = 1 };
1048:
1049: log_debug("%s: timer fired", __func__);
1050: evtimer_add(&cs->subs_timer, &tv);
1051:
1052: RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
1053: switch (csub->type) {
1054: case CONTROL_SUB_SESSION:
1055: control_check_subs_session(c, csub);
1056: break;
1057: case CONTROL_SUB_PANE:
1058: control_check_subs_pane(c, csub);
1059: break;
1060: case CONTROL_SUB_ALL_PANES:
1061: control_check_subs_all_panes(c, csub);
1062: break;
1063: case CONTROL_SUB_WINDOW:
1064: control_check_subs_window(c, csub);
1065: break;
1066: case CONTROL_SUB_ALL_WINDOWS:
1067: control_check_subs_all_windows(c, csub);
1068: break;
1069: }
1070: }
1071: }
1072:
1073: /* Add a subscription. */
1074: void
1075: control_add_sub(struct client *c, const char *name, enum control_sub_type type,
1076: int id, const char *format)
1077: {
1078: struct control_state *cs = c->control_state;
1079: struct control_sub *csub, find;
1080: struct timeval tv = { .tv_sec = 1 };
1081:
1082: find.name = (char *)name;
1083: if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
1084: control_free_sub(cs, csub);
1085:
1086: csub = xcalloc(1, sizeof *csub);
1087: csub->name = xstrdup(name);
1088: csub->type = type;
1089: csub->id = id;
1090: csub->format = xstrdup(format);
1091: RB_INSERT(control_subs, &cs->subs, csub);
1092:
1093: RB_INIT(&csub->panes);
1094: RB_INIT(&csub->windows);
1095:
1096: if (!evtimer_initialized(&cs->subs_timer))
1097: evtimer_set(&cs->subs_timer, control_check_subs_timer, c);
1098: if (!evtimer_pending(&cs->subs_timer, NULL))
1099: evtimer_add(&cs->subs_timer, &tv);
1100: }
1101:
1102: /* Remove a subscription. */
1103: void
1104: control_remove_sub(struct client *c, const char *name)
1105: {
1106: struct control_state *cs = c->control_state;
1107: struct control_sub *csub, find;
1108:
1109: find.name = (char *)name;
1110: if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
1111: control_free_sub(cs, csub);
1112: if (RB_EMPTY(&cs->subs))
1113: evtimer_del(&cs->subs_timer);
1.1 nicm 1114: }