[BACK]Return to control.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/control.c, Revision 1.41

1.41    ! nicm        1: /* $OpenBSD: control.c,v 1.40 2020/06/10 07:27:10 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.34      nicm       79: /* Control client state. */
1.33      nicm       80: struct control_state {
1.35      nicm       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;
1.34      nicm       87:
1.35      nicm       88:        struct bufferevent              *read_event;
                     89:        struct bufferevent              *write_event;
1.33      nicm       90: };
                     91:
1.40      nicm       92: /* Low and high watermarks. */
1.35      nicm       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:
1.40      nicm       99: /* Maximum age for clients that are not using pause mode. */
                    100: #define CONTROL_MAXIMUM_AGE 300000
                    101:
1.35      nicm      102: /* Flags to ignore client. */
                    103: #define CONTROL_IGNORE_FLAGS \
                    104:        (CLIENT_CONTROL_NOOUTPUT| \
                    105:         CLIENT_UNATTACHEDFLAGS)
                    106:
                    107: /* Compare client panes. */
1.32      nicm      108: static int
1.35      nicm      109: control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
1.32      nicm      110: {
1.35      nicm      111:        if (cp1->pane < cp2->pane)
1.32      nicm      112:                return (-1);
1.35      nicm      113:        if (cp1->pane > cp2->pane)
1.32      nicm      114:                return (1);
                    115:        return (0);
                    116: }
1.35      nicm      117: RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
                    118:
                    119: /* Free a block. */
                    120: static void
                    121: control_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: }
1.32      nicm      127:
                    128: /* Get pane offsets for this client. */
1.35      nicm      129: static struct control_pane *
                    130: control_get_pane(struct client *c, struct window_pane *wp)
1.32      nicm      131: {
1.33      nicm      132:        struct control_state    *cs = c->control_state;
1.35      nicm      133:        struct control_pane      cp = { .pane = wp->id };
1.32      nicm      134:
1.35      nicm      135:        return (RB_FIND(control_panes, &cs->panes, &cp));
1.32      nicm      136: }
                    137:
                    138: /* Add pane offsets for this client. */
1.35      nicm      139: static struct control_pane *
                    140: control_add_pane(struct client *c, struct window_pane *wp)
1.32      nicm      141: {
1.33      nicm      142:        struct control_state    *cs = c->control_state;
1.35      nicm      143:        struct control_pane     *cp;
                    144:
                    145:        cp = control_get_pane(c, wp);
                    146:        if (cp != NULL)
                    147:                return (cp);
1.32      nicm      148:
1.35      nicm      149:        cp = xcalloc(1, sizeof *cp);
                    150:        cp->pane = wp->id;
                    151:        RB_INSERT(control_panes, &cs->panes, cp);
1.32      nicm      152:
1.35      nicm      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);
1.32      nicm      158: }
                    159:
1.38      nicm      160: /* Discard output for a pane. */
                    161: static void
                    162: control_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:
1.37      nicm      173: /* Get actual pane for this client. */
                    174: static struct window_pane *
                    175: control_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:
1.35      nicm      188: /* Reset control offsets. */
1.32      nicm      189: void
1.35      nicm      190: control_reset_offsets(struct client *c)
1.32      nicm      191: {
1.33      nicm      192:        struct control_state    *cs = c->control_state;
1.35      nicm      193:        struct control_pane     *cp, *cp1;
1.32      nicm      194:
1.35      nicm      195:        RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) {
                    196:                RB_REMOVE(control_panes, &cs->panes, cp);
                    197:                free(cp);
1.32      nicm      198:        }
1.35      nicm      199:
                    200:        TAILQ_INIT(&cs->pending_list);
                    201:        cs->pending_count = 0;
1.32      nicm      202: }
                    203:
                    204: /* Get offsets for client. */
                    205: struct window_pane_offset *
                    206: control_pane_offset(struct client *c, struct window_pane *wp, int *off)
                    207: {
1.35      nicm      208:        struct control_state    *cs = c->control_state;
                    209:        struct control_pane     *cp;
1.32      nicm      210:
                    211:        if (c->flags & CLIENT_CONTROL_NOOUTPUT) {
                    212:                *off = 0;
                    213:                return (NULL);
                    214:        }
                    215:
1.35      nicm      216:        cp = control_get_pane(c, wp);
1.38      nicm      217:        if (cp == NULL || (cp->flags & CONTROL_PANE_PAUSED)) {
1.32      nicm      218:                *off = 0;
                    219:                return (NULL);
                    220:        }
1.35      nicm      221:        if (cp->flags & CONTROL_PANE_OFF) {
1.32      nicm      222:                *off = 1;
                    223:                return (NULL);
                    224:        }
1.35      nicm      225:        *off = (EVBUFFER_LENGTH(cs->write_event->output) >= CONTROL_BUFFER_LOW);
                    226:        return (&cp->offset);
1.32      nicm      227: }
                    228:
                    229: /* Set pane as on. */
                    230: void
                    231: control_set_pane_on(struct client *c, struct window_pane *wp)
                    232: {
1.35      nicm      233:        struct control_pane     *cp;
1.32      nicm      234:
1.35      nicm      235:        cp = control_get_pane(c, wp);
1.38      nicm      236:        if (cp != NULL && (cp->flags & CONTROL_PANE_OFF)) {
1.35      nicm      237:                cp->flags &= ~CONTROL_PANE_OFF;
                    238:                memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
                    239:                memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
1.32      nicm      240:        }
                    241: }
                    242:
                    243: /* Set pane as off. */
                    244: void
                    245: control_set_pane_off(struct client *c, struct window_pane *wp)
                    246: {
1.35      nicm      247:        struct control_pane     *cp;
1.32      nicm      248:
1.35      nicm      249:        cp = control_add_pane(c, wp);
                    250:        cp->flags |= CONTROL_PANE_OFF;
1.32      nicm      251: }
                    252:
1.38      nicm      253: /* Continue a paused pane. */
                    254: void
                    255: control_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);
1.41    ! nicm      265:        }
        !           266: }
        !           267:
        !           268: /* Pause a pane. */
        !           269: void
        !           270: control_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);
1.38      nicm      279:        }
                    280: }
                    281:
1.1       nicm      282: /* Write a line. */
1.35      nicm      283: static void
                    284: control_vwrite(struct client *c, const char *fmt, va_list ap)
1.1       nicm      285: {
1.34      nicm      286:        struct control_state    *cs = c->control_state;
                    287:        char                    *s;
1.1       nicm      288:
1.34      nicm      289:        xvasprintf(&s, fmt, ap);
1.35      nicm      290:        log_debug("%s: %s: writing line: %s", __func__, c->name, s);
1.34      nicm      291:
                    292:        bufferevent_write(cs->write_event, s, strlen(s));
                    293:        bufferevent_write(cs->write_event, "\n", 1);
1.35      nicm      294:
                    295:        bufferevent_enable(cs->write_event, EV_WRITE);
1.34      nicm      296:        free(s);
1.31      nicm      297: }
                    298:
1.35      nicm      299: /* Write a line. */
                    300: void
                    301: control_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);
1.37      nicm      318:        cb->t = get_timer();
1.35      nicm      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:
1.40      nicm      326: /* Check age for this pane. */
                    327: static int
                    328: control_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:
1.31      nicm      361: /* Write output from a pane. */
                    362: void
                    363: control_write_output(struct client *c, struct window_pane *wp)
                    364: {
1.34      nicm      365:        struct control_state    *cs = c->control_state;
1.35      nicm      366:        struct control_pane     *cp;
                    367:        struct control_block    *cb;
                    368:        size_t                   new_size;
1.31      nicm      369:
                    370:        if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
                    371:                return;
                    372:
1.35      nicm      373:        if (c->flags & CONTROL_IGNORE_FLAGS) {
                    374:                cp = control_get_pane(c, wp);
                    375:                if (cp != NULL)
                    376:                        goto ignore;
1.31      nicm      377:                return;
                    378:        }
1.35      nicm      379:        cp = control_add_pane(c, wp);
1.38      nicm      380:        if (cp->flags & (CONTROL_PANE_OFF|CONTROL_PANE_PAUSED))
1.35      nicm      381:                goto ignore;
1.40      nicm      382:        if (control_check_age(c, wp, cp))
                    383:                return;
1.35      nicm      384:
                    385:        window_pane_get_new_data(wp, &cp->queued, &new_size);
1.31      nicm      386:        if (new_size == 0)
                    387:                return;
1.35      nicm      388:        window_pane_update_used_data(wp, &cp->queued, new_size);
1.31      nicm      389:
1.35      nicm      390:        cb = xcalloc(1, sizeof *cb);
                    391:        cb->size = new_size;
                    392:        TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
1.37      nicm      393:        cb->t = get_timer();
1.35      nicm      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++;
1.31      nicm      405:        }
1.35      nicm      406:        bufferevent_enable(cs->write_event, EV_WRITE);
                    407:        return;
1.31      nicm      408:
1.35      nicm      409: ignore:
                    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);
1.1       nicm      413: }
                    414:
1.34      nicm      415: /* Control client error callback. */
1.18      nicm      416: static enum cmd_retval
1.19      nicm      417: control_error(struct cmdq_item *item, void *data)
1.18      nicm      418: {
1.26      nicm      419:        struct client   *c = cmdq_get_client(item);
1.18      nicm      420:        char            *error = data;
                    421:
1.19      nicm      422:        cmdq_guard(item, "begin", 1);
1.18      nicm      423:        control_write(c, "parse error: %s", error);
1.19      nicm      424:        cmdq_guard(item, "error", 1);
1.18      nicm      425:
                    426:        free(error);
                    427:        return (CMD_RETURN_NORMAL);
                    428: }
                    429:
1.34      nicm      430: /* Control client error callback. */
1.25      nicm      431: static void
1.34      nicm      432: control_error_callback(__unused struct bufferevent *bufev,
                    433:     __unused short what, void *data)
1.1       nicm      434: {
1.34      nicm      435:        struct client   *c = data;
                    436:
                    437:        c->flags |= CLIENT_EXIT;
                    438: }
                    439:
                    440: /* Control client input callback. Read lines and fire commands. */
                    441: static void
                    442: control_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;
1.30      nicm      447:        char                    *line, *error;
1.29      nicm      448:        struct cmdq_state       *state;
1.30      nicm      449:        enum cmd_parse_status    status;
1.1       nicm      450:
                    451:        for (;;) {
1.25      nicm      452:                line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
1.1       nicm      453:                if (line == NULL)
                    454:                        break;
1.35      nicm      455:                log_debug("%s: %s: %s", __func__, c->name, line);
                    456:                if (*line == '\0') { /* empty line detach */
1.24      nicm      457:                        free(line);
1.1       nicm      458:                        c->flags |= CLIENT_EXIT;
                    459:                        break;
                    460:                }
                    461:
1.30      nicm      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);
1.1       nicm      467:
1.2       nicm      468:                free(line);
1.1       nicm      469:        }
1.25      nicm      470: }
                    471:
1.35      nicm      472: /* Does this control client have outstanding data to write? */
                    473: int
                    474: control_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. */
                    484: static void
                    485: control_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. */
                    503: static struct evbuffer *
1.39      nicm      504: control_append_data(struct client *c, struct control_pane *cp, uint64_t age,
                    505:     struct evbuffer *message, struct window_pane *wp, size_t size)
1.35      nicm      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");
1.39      nicm      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);
1.35      nicm      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. */
                    537: static void
                    538: control_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. */
                    551: static int
                    552: control_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;
1.39      nicm      559:        uint64_t                 age, t = get_timer();
1.35      nicm      560:
1.37      nicm      561:        wp = control_window_pane(c, cp->pane);
                    562:        if (wp == NULL) {
1.36      nicm      563:                TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) {
                    564:                        TAILQ_REMOVE(&cp->blocks, cb, entry);
1.35      nicm      565:                        control_free_block(cs, cb);
1.36      nicm      566:                }
1.35      nicm      567:                control_flush_all_blocks(c);
                    568:                return (0);
                    569:        }
                    570:
                    571:        while (used != limit && !TAILQ_EMPTY(&cp->blocks)) {
                    572:                cb = TAILQ_FIRST(&cp->blocks);
1.39      nicm      573:                if (cb->t < t)
                    574:                        age = t - cb->t;
                    575:                else
                    576:                        age = 0;
                    577:                log_debug("%s: %s: output block %zu (age %llu) for %%%u "
                    578:                    "(used %zu/%zu)", __func__, c->name, cb->size, age,
                    579:                    cp->pane, used, limit);
1.35      nicm      580:
                    581:                size = cb->size;
                    582:                if (size > limit - used)
                    583:                        size = limit - used;
                    584:                used += size;
                    585:
1.39      nicm      586:                message = control_append_data(c, cp, age, message, wp, size);
1.35      nicm      587:
                    588:                cb->size -= size;
                    589:                if (cb->size == 0) {
                    590:                        TAILQ_REMOVE(&cp->blocks, cb, entry);
                    591:                        control_free_block(cs, cb);
                    592:
                    593:                        cb = TAILQ_FIRST(&cs->all_blocks);
                    594:                        if (cb != NULL && cb->size == 0) {
                    595:                                if (wp != NULL && message != NULL) {
                    596:                                        control_write_data(c, message);
                    597:                                        message = NULL;
                    598:                                }
                    599:                                control_flush_all_blocks(c);
                    600:                        }
                    601:                }
                    602:        }
                    603:        if (message != NULL)
                    604:                control_write_data(c, message);
                    605:        return (!TAILQ_EMPTY(&cp->blocks));
                    606: }
                    607:
                    608: /* Control client write callback. */
                    609: static void
                    610: control_write_callback(__unused struct bufferevent *bufev, void *data)
                    611: {
                    612:        struct client           *c = data;
                    613:        struct control_state    *cs = c->control_state;
                    614:        struct control_pane     *cp, *cp1;
                    615:        struct evbuffer         *evb = cs->write_event->output;
                    616:        size_t                   space, limit;
                    617:
                    618:        control_flush_all_blocks(c);
                    619:
                    620:        while (EVBUFFER_LENGTH(evb) < CONTROL_BUFFER_HIGH) {
                    621:                if (cs->pending_count == 0)
                    622:                        break;
                    623:                space = CONTROL_BUFFER_HIGH - EVBUFFER_LENGTH(evb);
                    624:                log_debug("%s: %s: %zu bytes available, %u panes", __func__,
                    625:                    c->name, space, cs->pending_count);
                    626:
                    627:                limit = (space / cs->pending_count / 3); /* 3 bytes for \xxx */
                    628:                if (limit < CONTROL_WRITE_MINIMUM)
                    629:                        limit = CONTROL_WRITE_MINIMUM;
                    630:
                    631:                TAILQ_FOREACH_SAFE(cp, &cs->pending_list, pending_entry, cp1) {
                    632:                        if (EVBUFFER_LENGTH(evb) >= CONTROL_BUFFER_HIGH)
                    633:                                break;
                    634:                        if (control_write_pending(c, cp, limit))
                    635:                                continue;
                    636:                        TAILQ_REMOVE(&cs->pending_list, cp, pending_entry);
                    637:                        cp->pending_flag = 0;
                    638:                        cs->pending_count--;
                    639:                }
                    640:        }
                    641:        if (EVBUFFER_LENGTH(evb) == 0)
                    642:                bufferevent_disable(cs->write_event, EV_WRITE);
                    643: }
                    644:
1.32      nicm      645: /* Initialize for control mode. */
1.25      nicm      646: void
                    647: control_start(struct client *c)
                    648: {
1.33      nicm      649:        struct control_state    *cs;
                    650:
1.34      nicm      651:        if (c->flags & CLIENT_CONTROLCONTROL) {
                    652:                close(c->out_fd);
                    653:                c->out_fd = -1;
                    654:        } else
                    655:                setblocking(c->out_fd, 0);
                    656:        setblocking(c->fd, 0);
                    657:
1.33      nicm      658:        cs = c->control_state = xcalloc(1, sizeof *cs);
1.35      nicm      659:        RB_INIT(&cs->panes);
                    660:        TAILQ_INIT(&cs->pending_list);
                    661:        TAILQ_INIT(&cs->all_blocks);
1.33      nicm      662:
1.35      nicm      663:        cs->read_event = bufferevent_new(c->fd, control_read_callback,
                    664:            control_write_callback, control_error_callback, c);
1.34      nicm      665:        bufferevent_enable(cs->read_event, EV_READ);
1.25      nicm      666:
                    667:        if (c->flags & CLIENT_CONTROLCONTROL)
1.34      nicm      668:                cs->write_event = cs->read_event;
                    669:        else {
1.35      nicm      670:                cs->write_event = bufferevent_new(c->out_fd, NULL,
                    671:                    control_write_callback, control_error_callback, c);
                    672:        }
                    673:        bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
                    674:            0);
                    675:
                    676:        if (c->flags & CLIENT_CONTROLCONTROL) {
                    677:                bufferevent_write(cs->write_event, "\033P1000p", 7);
                    678:                bufferevent_enable(cs->write_event, EV_WRITE);
1.34      nicm      679:        }
1.35      nicm      680: }
                    681:
1.38      nicm      682: /* Discard all output for a client. */
1.35      nicm      683: void
1.38      nicm      684: control_discard(struct client *c)
1.35      nicm      685: {
                    686:        struct control_state    *cs = c->control_state;
                    687:        struct control_pane     *cp;
1.34      nicm      688:
1.38      nicm      689:        RB_FOREACH(cp, control_panes, &cs->panes)
                    690:                control_discard_pane(c, cp);
1.33      nicm      691: }
                    692:
                    693: /* Stop control mode. */
                    694: void
                    695: control_stop(struct client *c)
                    696: {
                    697:        struct control_state    *cs = c->control_state;
1.35      nicm      698:        struct control_block    *cb, *cb1;
1.34      nicm      699:
                    700:        if (~c->flags & CLIENT_CONTROLCONTROL)
                    701:                bufferevent_free(cs->write_event);
                    702:        bufferevent_free(cs->read_event);
1.33      nicm      703:
1.35      nicm      704:        TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
                    705:                control_free_block(cs, cb);
                    706:        control_reset_offsets(c);
                    707:
1.33      nicm      708:        free(cs);
1.1       nicm      709: }