control.c revision 1.42
1/* $OpenBSD: control.c,v 1.42 2020/06/12 08:35:01 nicm Exp $ */ 2 3/* 4 * Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * Copyright (c) 2012 George Nachman <tmux@georgester.com> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 16 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 17 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <sys/types.h> 21 22#include <event.h> 23#include <stdlib.h> 24#include <string.h> 25#include <time.h> 26#include <unistd.h> 27 28#include "tmux.h" 29 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 */ 45struct control_block { 46 size_t size; 47 char *line; 48 uint64_t t; 49 50 TAILQ_ENTRY(control_block) entry; 51 TAILQ_ENTRY(control_block) all_entry; 52}; 53 54/* Control client pane. */ 55struct 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 68#define CONTROL_PANE_PAUSED 0x2 69 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; 76}; 77RB_HEAD(control_panes, control_pane); 78 79/* Control client state. */ 80struct control_state { 81 struct control_panes panes; 82 83 TAILQ_HEAD(, control_pane) pending_list; 84 u_int pending_count; 85 86 TAILQ_HEAD(, control_block) all_blocks; 87 88 struct bufferevent *read_event; 89 struct bufferevent *write_event; 90}; 91 92/* Low and high watermarks. */ 93#define CONTROL_BUFFER_LOW 512 94#define CONTROL_BUFFER_HIGH 8192 95 96/* Minimum to write to each client. */ 97#define CONTROL_WRITE_MINIMUM 32 98 99/* Maximum age for clients that are not using pause mode. */ 100#define CONTROL_MAXIMUM_AGE 300000 101 102/* Flags to ignore client. */ 103#define CONTROL_IGNORE_FLAGS \ 104 (CLIENT_CONTROL_NOOUTPUT| \ 105 CLIENT_UNATTACHEDFLAGS) 106 107/* Compare client panes. */ 108static int 109control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2) 110{ 111 if (cp1->pane < cp2->pane) 112 return (-1); 113 if (cp1->pane > cp2->pane) 114 return (1); 115 return (0); 116} 117RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp); 118 119/* Free a block. */ 120static void 121control_free_block(struct control_state *cs, struct control_block *cb) 122{ 123 free(cb->line); 124 TAILQ_REMOVE(&cs->all_blocks, cb, all_entry); 125 free(cb); 126} 127 128/* Get pane offsets for this client. */ 129static struct control_pane * 130control_get_pane(struct client *c, struct window_pane *wp) 131{ 132 struct control_state *cs = c->control_state; 133 struct control_pane cp = { .pane = wp->id }; 134 135 return (RB_FIND(control_panes, &cs->panes, &cp)); 136} 137 138/* Add pane offsets for this client. */ 139static struct control_pane * 140control_add_pane(struct client *c, struct window_pane *wp) 141{ 142 struct control_state *cs = c->control_state; 143 struct control_pane *cp; 144 145 cp = control_get_pane(c, wp); 146 if (cp != NULL) 147 return (cp); 148 149 cp = xcalloc(1, sizeof *cp); 150 cp->pane = wp->id; 151 RB_INSERT(control_panes, &cs->panes, cp); 152 153 memcpy(&cp->offset, &wp->offset, sizeof cp->offset); 154 memcpy(&cp->queued, &wp->offset, sizeof cp->queued); 155 TAILQ_INIT(&cp->blocks); 156 157 return (cp); 158} 159 160/* Discard output for a pane. */ 161static void 162control_discard_pane(struct client *c, struct control_pane *cp) 163{ 164 struct control_state *cs = c->control_state; 165 struct control_block *cb, *cb1; 166 167 TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { 168 TAILQ_REMOVE(&cp->blocks, cb, entry); 169 control_free_block(cs, cb); 170 } 171} 172 173/* Get actual pane for this client. */ 174static struct window_pane * 175control_window_pane(struct client *c, u_int pane) 176{ 177 struct window_pane *wp; 178 179 if (c->session == NULL) 180 return (NULL); 181 if ((wp = window_pane_find_by_id(pane)) == NULL) 182 return (NULL); 183 if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) 184 return (NULL); 185 return (wp); 186} 187 188/* Reset control offsets. */ 189void 190control_reset_offsets(struct client *c) 191{ 192 struct control_state *cs = c->control_state; 193 struct control_pane *cp, *cp1; 194 195 RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) { 196 RB_REMOVE(control_panes, &cs->panes, cp); 197 free(cp); 198 } 199 200 TAILQ_INIT(&cs->pending_list); 201 cs->pending_count = 0; 202} 203 204/* Get offsets for client. */ 205struct window_pane_offset * 206control_pane_offset(struct client *c, struct window_pane *wp, int *off) 207{ 208 struct control_state *cs = c->control_state; 209 struct control_pane *cp; 210 211 if (c->flags & CLIENT_CONTROL_NOOUTPUT) { 212 *off = 0; 213 return (NULL); 214 } 215 216 cp = control_get_pane(c, wp); 217 if (cp == NULL || (cp->flags & CONTROL_PANE_PAUSED)) { 218 *off = 0; 219 return (NULL); 220 } 221 if (cp->flags & CONTROL_PANE_OFF) { 222 *off = 1; 223 return (NULL); 224 } 225 *off = (EVBUFFER_LENGTH(cs->write_event->output) >= CONTROL_BUFFER_LOW); 226 return (&cp->offset); 227} 228 229/* Set pane as on. */ 230void 231control_set_pane_on(struct client *c, struct window_pane *wp) 232{ 233 struct control_pane *cp; 234 235 cp = control_get_pane(c, wp); 236 if (cp != NULL && (cp->flags & CONTROL_PANE_OFF)) { 237 cp->flags &= ~CONTROL_PANE_OFF; 238 memcpy(&cp->offset, &wp->offset, sizeof cp->offset); 239 memcpy(&cp->queued, &wp->offset, sizeof cp->queued); 240 } 241} 242 243/* Set pane as off. */ 244void 245control_set_pane_off(struct client *c, struct window_pane *wp) 246{ 247 struct control_pane *cp; 248 249 cp = control_add_pane(c, wp); 250 cp->flags |= CONTROL_PANE_OFF; 251} 252 253/* Continue a paused pane. */ 254void 255control_continue_pane(struct client *c, struct window_pane *wp) 256{ 257 struct control_pane *cp; 258 259 cp = control_get_pane(c, wp); 260 if (cp != NULL && (cp->flags & CONTROL_PANE_PAUSED)) { 261 cp->flags &= ~CONTROL_PANE_PAUSED; 262 memcpy(&cp->offset, &wp->offset, sizeof cp->offset); 263 memcpy(&cp->queued, &wp->offset, sizeof cp->queued); 264 control_write(c, "%%continue %%%u", wp->id); 265 } 266} 267 268/* Pause a pane. */ 269void 270control_pause_pane(struct client *c, struct window_pane *wp) 271{ 272 struct control_pane *cp; 273 274 cp = control_add_pane(c, wp); 275 if (~cp->flags & CONTROL_PANE_PAUSED) { 276 cp->flags |= CONTROL_PANE_PAUSED; 277 control_discard_pane(c, cp); 278 control_write(c, "%%pause %%%u", wp->id); 279 } 280} 281 282/* Write a line. */ 283static void 284control_vwrite(struct client *c, const char *fmt, va_list ap) 285{ 286 struct control_state *cs = c->control_state; 287 char *s; 288 289 xvasprintf(&s, fmt, ap); 290 log_debug("%s: %s: writing line: %s", __func__, c->name, s); 291 292 bufferevent_write(cs->write_event, s, strlen(s)); 293 bufferevent_write(cs->write_event, "\n", 1); 294 295 bufferevent_enable(cs->write_event, EV_WRITE); 296 free(s); 297} 298 299/* Write a line. */ 300void 301control_write(struct client *c, const char *fmt, ...) 302{ 303 struct control_state *cs = c->control_state; 304 struct control_block *cb; 305 va_list ap; 306 307 va_start(ap, fmt); 308 309 if (TAILQ_EMPTY(&cs->all_blocks)) { 310 control_vwrite(c, fmt, ap); 311 va_end(ap); 312 return; 313 } 314 315 cb = xcalloc(1, sizeof *cb); 316 xvasprintf(&cb->line, fmt, ap); 317 TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); 318 cb->t = get_timer(); 319 320 log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line); 321 bufferevent_enable(cs->write_event, EV_WRITE); 322 323 va_end(ap); 324} 325 326/* Check age for this pane. */ 327static int 328control_check_age(struct client *c, struct window_pane *wp, 329 struct control_pane *cp) 330{ 331 struct control_block *cb; 332 uint64_t t, age; 333 334 cb = TAILQ_FIRST(&cp->blocks); 335 if (cb == NULL) 336 return (0); 337 t = get_timer(); 338 if (cb->t >= t) 339 return (0); 340 341 age = t - cb->t; 342 log_debug("%s: %s: %%%u is %llu behind", __func__, c->name, wp->id, 343 (unsigned long long)age); 344 345 if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { 346 if (age < c->pause_age) 347 return (0); 348 cp->flags |= CONTROL_PANE_PAUSED; 349 control_discard_pane(c, cp); 350 control_write(c, "%%pause %%%u", wp->id); 351 } else { 352 if (age < CONTROL_MAXIMUM_AGE) 353 return (0); 354 c->exit_message = xstrdup("too far behind"); 355 c->flags |= CLIENT_EXIT; 356 control_discard(c); 357 } 358 return (1); 359} 360 361/* Write output from a pane. */ 362void 363control_write_output(struct client *c, struct window_pane *wp) 364{ 365 struct control_state *cs = c->control_state; 366 struct control_pane *cp; 367 struct control_block *cb; 368 size_t new_size; 369 370 if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) 371 return; 372 373 if (c->flags & CONTROL_IGNORE_FLAGS) { 374 cp = control_get_pane(c, wp); 375 if (cp != NULL) 376 goto ignore; 377 return; 378 } 379 cp = control_add_pane(c, wp); 380 if (cp->flags & (CONTROL_PANE_OFF|CONTROL_PANE_PAUSED)) 381 goto ignore; 382 if (control_check_age(c, wp, cp)) 383 return; 384 385 window_pane_get_new_data(wp, &cp->queued, &new_size); 386 if (new_size == 0) 387 return; 388 window_pane_update_used_data(wp, &cp->queued, new_size); 389 390 cb = xcalloc(1, sizeof *cb); 391 cb->size = new_size; 392 TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); 393 cb->t = get_timer(); 394 395 TAILQ_INSERT_TAIL(&cp->blocks, cb, entry); 396 log_debug("%s: %s: new output block of %zu for %%%u", __func__, c->name, 397 cb->size, wp->id); 398 399 if (!cp->pending_flag) { 400 log_debug("%s: %s: %%%u now pending", __func__, c->name, 401 wp->id); 402 TAILQ_INSERT_TAIL(&cs->pending_list, cp, pending_entry); 403 cp->pending_flag = 1; 404 cs->pending_count++; 405 } 406 bufferevent_enable(cs->write_event, EV_WRITE); 407 return; 408 409ignore: 410 log_debug("%s: %s: ignoring pane %%%u", __func__, c->name, wp->id); 411 window_pane_update_used_data(wp, &cp->offset, SIZE_MAX); 412 window_pane_update_used_data(wp, &cp->queued, SIZE_MAX); 413} 414 415/* Control client error callback. */ 416static enum cmd_retval 417control_error(struct cmdq_item *item, void *data) 418{ 419 struct client *c = cmdq_get_client(item); 420 char *error = data; 421 422 cmdq_guard(item, "begin", 1); 423 control_write(c, "parse error: %s", error); 424 cmdq_guard(item, "error", 1); 425 426 free(error); 427 return (CMD_RETURN_NORMAL); 428} 429 430/* Control client error callback. */ 431static void 432control_error_callback(__unused struct bufferevent *bufev, 433 __unused short what, void *data) 434{ 435 struct client *c = data; 436 437 c->flags |= CLIENT_EXIT; 438} 439 440/* Control client input callback. Read lines and fire commands. */ 441static void 442control_read_callback(__unused struct bufferevent *bufev, void *data) 443{ 444 struct client *c = data; 445 struct control_state *cs = c->control_state; 446 struct evbuffer *buffer = cs->read_event->input; 447 char *line, *error; 448 struct cmdq_state *state; 449 enum cmd_parse_status status; 450 451 for (;;) { 452 line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF); 453 if (line == NULL) 454 break; 455 log_debug("%s: %s: %s", __func__, c->name, line); 456 if (*line == '\0') { /* empty line detach */ 457 free(line); 458 c->flags |= CLIENT_EXIT; 459 break; 460 } 461 462 state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL); 463 status = cmd_parse_and_append(line, NULL, c, state, &error); 464 if (status == CMD_PARSE_ERROR) 465 cmdq_append(c, cmdq_get_callback(control_error, error)); 466 cmdq_free_state(state); 467 468 free(line); 469 } 470} 471 472/* Does this control client have outstanding data to write? */ 473int 474control_all_done(struct client *c) 475{ 476 struct control_state *cs = c->control_state; 477 478 if (!TAILQ_EMPTY(&cs->all_blocks)) 479 return (0); 480 return (EVBUFFER_LENGTH(cs->write_event->output) == 0); 481} 482 483/* Flush all blocks until output. */ 484static void 485control_flush_all_blocks(struct client *c) 486{ 487 struct control_state *cs = c->control_state; 488 struct control_block *cb, *cb1; 489 490 TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) { 491 if (cb->size != 0) 492 break; 493 log_debug("%s: %s: flushing line: %s", __func__, c->name, 494 cb->line); 495 496 bufferevent_write(cs->write_event, cb->line, strlen(cb->line)); 497 bufferevent_write(cs->write_event, "\n", 1); 498 control_free_block(cs, cb); 499 } 500} 501 502/* Append data to buffer. */ 503static struct evbuffer * 504control_append_data(struct client *c, struct control_pane *cp, uint64_t age, 505 struct evbuffer *message, struct window_pane *wp, size_t size) 506{ 507 u_char *new_data; 508 size_t new_size; 509 u_int i; 510 511 if (message == NULL) { 512 message = evbuffer_new(); 513 if (message == NULL) 514 fatalx("out of memory"); 515 if (c->flags & CLIENT_CONTROL_PAUSEAFTER) { 516 evbuffer_add_printf(message, 517 "%%extended-output %%%u %llu : ", wp->id, 518 (unsigned long long)age); 519 } else 520 evbuffer_add_printf(message, "%%output %%%u ", wp->id); 521 } 522 523 new_data = window_pane_get_new_data(wp, &cp->offset, &new_size); 524 if (new_size < size) 525 fatalx("not enough data: %zu < %zu", new_size, size); 526 for (i = 0; i < size; i++) { 527 if (new_data[i] < ' ' || new_data[i] == '\\') 528 evbuffer_add_printf(message, "\\%03o", new_data[i]); 529 else 530 evbuffer_add_printf(message, "%c", new_data[i]); 531 } 532 window_pane_update_used_data(wp, &cp->offset, size); 533 return (message); 534} 535 536/* Write buffer. */ 537static void 538control_write_data(struct client *c, struct evbuffer *message) 539{ 540 struct control_state *cs = c->control_state; 541 542 log_debug("%s: %s: %.*s", __func__, c->name, 543 (int)EVBUFFER_LENGTH(message), EVBUFFER_DATA(message)); 544 545 evbuffer_add(message, "\n", 1); 546 bufferevent_write_buffer(cs->write_event, message); 547 evbuffer_free(message); 548} 549 550/* Write output to client. */ 551static int 552control_write_pending(struct client *c, struct control_pane *cp, size_t limit) 553{ 554 struct control_state *cs = c->control_state; 555 struct window_pane *wp = NULL; 556 struct evbuffer *message = NULL; 557 size_t used = 0, size; 558 struct control_block *cb, *cb1; 559 uint64_t age, t = get_timer(); 560 561 wp = control_window_pane(c, cp->pane); 562 if (wp == NULL) { 563 TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) { 564 TAILQ_REMOVE(&cp->blocks, cb, entry); 565 control_free_block(cs, cb); 566 } 567 control_flush_all_blocks(c); 568 return (0); 569 } 570 571 while (used != limit && !TAILQ_EMPTY(&cp->blocks)) { 572 if (control_check_age(c, wp, cp)) { 573 if (message != NULL) 574 evbuffer_free(message); 575 message = NULL; 576 break; 577 } 578 579 cb = TAILQ_FIRST(&cp->blocks); 580 if (cb->t < t) 581 age = t - cb->t; 582 else 583 age = 0; 584 log_debug("%s: %s: output block %zu (age %llu) for %%%u " 585 "(used %zu/%zu)", __func__, c->name, cb->size, age, 586 cp->pane, used, limit); 587 588 size = cb->size; 589 if (size > limit - used) 590 size = limit - used; 591 used += size; 592 593 message = control_append_data(c, cp, age, message, wp, size); 594 595 cb->size -= size; 596 if (cb->size == 0) { 597 TAILQ_REMOVE(&cp->blocks, cb, entry); 598 control_free_block(cs, cb); 599 600 cb = TAILQ_FIRST(&cs->all_blocks); 601 if (cb != NULL && cb->size == 0) { 602 if (wp != NULL && message != NULL) { 603 control_write_data(c, message); 604 message = NULL; 605 } 606 control_flush_all_blocks(c); 607 } 608 } 609 } 610 if (message != NULL) 611 control_write_data(c, message); 612 return (!TAILQ_EMPTY(&cp->blocks)); 613} 614 615/* Control client write callback. */ 616static void 617control_write_callback(__unused struct bufferevent *bufev, void *data) 618{ 619 struct client *c = data; 620 struct control_state *cs = c->control_state; 621 struct control_pane *cp, *cp1; 622 struct evbuffer *evb = cs->write_event->output; 623 size_t space, limit; 624 625 control_flush_all_blocks(c); 626 627 while (EVBUFFER_LENGTH(evb) < CONTROL_BUFFER_HIGH) { 628 if (cs->pending_count == 0) 629 break; 630 space = CONTROL_BUFFER_HIGH - EVBUFFER_LENGTH(evb); 631 log_debug("%s: %s: %zu bytes available, %u panes", __func__, 632 c->name, space, cs->pending_count); 633 634 limit = (space / cs->pending_count / 3); /* 3 bytes for \xxx */ 635 if (limit < CONTROL_WRITE_MINIMUM) 636 limit = CONTROL_WRITE_MINIMUM; 637 638 TAILQ_FOREACH_SAFE(cp, &cs->pending_list, pending_entry, cp1) { 639 if (EVBUFFER_LENGTH(evb) >= CONTROL_BUFFER_HIGH) 640 break; 641 if (control_write_pending(c, cp, limit)) 642 continue; 643 TAILQ_REMOVE(&cs->pending_list, cp, pending_entry); 644 cp->pending_flag = 0; 645 cs->pending_count--; 646 } 647 } 648 if (EVBUFFER_LENGTH(evb) == 0) 649 bufferevent_disable(cs->write_event, EV_WRITE); 650} 651 652/* Initialize for control mode. */ 653void 654control_start(struct client *c) 655{ 656 struct control_state *cs; 657 658 if (c->flags & CLIENT_CONTROLCONTROL) { 659 close(c->out_fd); 660 c->out_fd = -1; 661 } else 662 setblocking(c->out_fd, 0); 663 setblocking(c->fd, 0); 664 665 cs = c->control_state = xcalloc(1, sizeof *cs); 666 RB_INIT(&cs->panes); 667 TAILQ_INIT(&cs->pending_list); 668 TAILQ_INIT(&cs->all_blocks); 669 670 cs->read_event = bufferevent_new(c->fd, control_read_callback, 671 control_write_callback, control_error_callback, c); 672 bufferevent_enable(cs->read_event, EV_READ); 673 674 if (c->flags & CLIENT_CONTROLCONTROL) 675 cs->write_event = cs->read_event; 676 else { 677 cs->write_event = bufferevent_new(c->out_fd, NULL, 678 control_write_callback, control_error_callback, c); 679 } 680 bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW, 681 0); 682 683 if (c->flags & CLIENT_CONTROLCONTROL) { 684 bufferevent_write(cs->write_event, "\033P1000p", 7); 685 bufferevent_enable(cs->write_event, EV_WRITE); 686 } 687} 688 689/* Discard all output for a client. */ 690void 691control_discard(struct client *c) 692{ 693 struct control_state *cs = c->control_state; 694 struct control_pane *cp; 695 696 RB_FOREACH(cp, control_panes, &cs->panes) 697 control_discard_pane(c, cp); 698} 699 700/* Stop control mode. */ 701void 702control_stop(struct client *c) 703{ 704 struct control_state *cs = c->control_state; 705 struct control_block *cb, *cb1; 706 707 if (~c->flags & CLIENT_CONTROLCONTROL) 708 bufferevent_free(cs->write_event); 709 bufferevent_free(cs->read_event); 710 711 TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) 712 control_free_block(cs, cb); 713 control_reset_offsets(c); 714 715 free(cs); 716} 717