[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.38

1.38    ! nicm        1: /* $OpenBSD: control.c,v 1.37 2020/06/02 08:17:27 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.35      nicm       92: /* Low watermark. */
                     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: /* Flags to ignore client. */
                    100: #define CONTROL_IGNORE_FLAGS \
                    101:        (CLIENT_CONTROL_NOOUTPUT| \
                    102:         CLIENT_UNATTACHEDFLAGS)
                    103:
                    104: /* Compare client panes. */
1.32      nicm      105: static int
1.35      nicm      106: control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
1.32      nicm      107: {
1.35      nicm      108:        if (cp1->pane < cp2->pane)
1.32      nicm      109:                return (-1);
1.35      nicm      110:        if (cp1->pane > cp2->pane)
1.32      nicm      111:                return (1);
                    112:        return (0);
                    113: }
1.35      nicm      114: RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
                    115:
                    116: /* Free a block. */
                    117: static void
                    118: control_free_block(struct control_state *cs, struct control_block *cb)
                    119: {
                    120:        free(cb->line);
                    121:        TAILQ_REMOVE(&cs->all_blocks, cb, all_entry);
                    122:        free(cb);
                    123: }
1.32      nicm      124:
                    125: /* Get pane offsets for this client. */
1.35      nicm      126: static struct control_pane *
                    127: control_get_pane(struct client *c, struct window_pane *wp)
1.32      nicm      128: {
1.33      nicm      129:        struct control_state    *cs = c->control_state;
1.35      nicm      130:        struct control_pane      cp = { .pane = wp->id };
1.32      nicm      131:
1.35      nicm      132:        return (RB_FIND(control_panes, &cs->panes, &cp));
1.32      nicm      133: }
                    134:
                    135: /* Add pane offsets for this client. */
1.35      nicm      136: static struct control_pane *
                    137: control_add_pane(struct client *c, struct window_pane *wp)
1.32      nicm      138: {
1.33      nicm      139:        struct control_state    *cs = c->control_state;
1.35      nicm      140:        struct control_pane     *cp;
                    141:
                    142:        cp = control_get_pane(c, wp);
                    143:        if (cp != NULL)
                    144:                return (cp);
1.32      nicm      145:
1.35      nicm      146:        cp = xcalloc(1, sizeof *cp);
                    147:        cp->pane = wp->id;
                    148:        RB_INSERT(control_panes, &cs->panes, cp);
1.32      nicm      149:
1.35      nicm      150:        memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
                    151:        memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
                    152:        TAILQ_INIT(&cp->blocks);
                    153:
                    154:        return (cp);
1.32      nicm      155: }
                    156:
1.38    ! nicm      157: /* Discard output for a pane. */
        !           158: static void
        !           159: control_discard_pane(struct client *c, struct control_pane *cp)
        !           160: {
        !           161:        struct control_state    *cs = c->control_state;
        !           162:        struct control_block    *cb, *cb1;
        !           163:
        !           164:        TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) {
        !           165:                TAILQ_REMOVE(&cp->blocks, cb, entry);
        !           166:                control_free_block(cs, cb);
        !           167:        }
        !           168: }
        !           169:
1.37      nicm      170: /* Get actual pane for this client. */
                    171: static struct window_pane *
                    172: control_window_pane(struct client *c, u_int pane)
                    173: {
                    174:        struct window_pane      *wp;
                    175:
                    176:        if (c->session == NULL)
                    177:                return (NULL);
                    178:        if ((wp = window_pane_find_by_id(pane)) == NULL)
                    179:                return (NULL);
                    180:        if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
                    181:                return (NULL);
                    182:        return (wp);
                    183: }
                    184:
1.35      nicm      185: /* Reset control offsets. */
1.32      nicm      186: void
1.35      nicm      187: control_reset_offsets(struct client *c)
1.32      nicm      188: {
1.33      nicm      189:        struct control_state    *cs = c->control_state;
1.35      nicm      190:        struct control_pane     *cp, *cp1;
1.32      nicm      191:
1.35      nicm      192:        RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) {
                    193:                RB_REMOVE(control_panes, &cs->panes, cp);
                    194:                free(cp);
1.32      nicm      195:        }
1.35      nicm      196:
                    197:        TAILQ_INIT(&cs->pending_list);
                    198:        cs->pending_count = 0;
1.32      nicm      199: }
                    200:
                    201: /* Get offsets for client. */
                    202: struct window_pane_offset *
                    203: control_pane_offset(struct client *c, struct window_pane *wp, int *off)
                    204: {
1.35      nicm      205:        struct control_state    *cs = c->control_state;
                    206:        struct control_pane     *cp;
1.32      nicm      207:
                    208:        if (c->flags & CLIENT_CONTROL_NOOUTPUT) {
                    209:                *off = 0;
                    210:                return (NULL);
                    211:        }
                    212:
1.35      nicm      213:        cp = control_get_pane(c, wp);
1.38    ! nicm      214:        if (cp == NULL || (cp->flags & CONTROL_PANE_PAUSED)) {
1.32      nicm      215:                *off = 0;
                    216:                return (NULL);
                    217:        }
1.35      nicm      218:        if (cp->flags & CONTROL_PANE_OFF) {
1.32      nicm      219:                *off = 1;
                    220:                return (NULL);
                    221:        }
1.35      nicm      222:        *off = (EVBUFFER_LENGTH(cs->write_event->output) >= CONTROL_BUFFER_LOW);
                    223:        return (&cp->offset);
1.32      nicm      224: }
                    225:
                    226: /* Set pane as on. */
                    227: void
                    228: control_set_pane_on(struct client *c, struct window_pane *wp)
                    229: {
1.35      nicm      230:        struct control_pane     *cp;
1.32      nicm      231:
1.35      nicm      232:        cp = control_get_pane(c, wp);
1.38    ! nicm      233:        if (cp != NULL && (cp->flags & CONTROL_PANE_OFF)) {
1.35      nicm      234:                cp->flags &= ~CONTROL_PANE_OFF;
                    235:                memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
                    236:                memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
1.32      nicm      237:        }
                    238: }
                    239:
                    240: /* Set pane as off. */
                    241: void
                    242: control_set_pane_off(struct client *c, struct window_pane *wp)
                    243: {
1.35      nicm      244:        struct control_pane     *cp;
1.32      nicm      245:
1.35      nicm      246:        cp = control_add_pane(c, wp);
                    247:        cp->flags |= CONTROL_PANE_OFF;
1.32      nicm      248: }
                    249:
1.38    ! nicm      250: /* Continue a paused pane. */
        !           251: void
        !           252: control_continue_pane(struct client *c, struct window_pane *wp)
        !           253: {
        !           254:        struct control_pane     *cp;
        !           255:
        !           256:        cp = control_get_pane(c, wp);
        !           257:        if (cp != NULL && (cp->flags & CONTROL_PANE_PAUSED)) {
        !           258:                cp->flags &= ~CONTROL_PANE_PAUSED;
        !           259:                memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
        !           260:                memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
        !           261:                control_write(c, "%%continue %%%u", wp->id);
        !           262:        }
        !           263: }
        !           264:
1.1       nicm      265: /* Write a line. */
1.35      nicm      266: static void
                    267: control_vwrite(struct client *c, const char *fmt, va_list ap)
1.1       nicm      268: {
1.34      nicm      269:        struct control_state    *cs = c->control_state;
                    270:        char                    *s;
1.1       nicm      271:
1.34      nicm      272:        xvasprintf(&s, fmt, ap);
1.35      nicm      273:        log_debug("%s: %s: writing line: %s", __func__, c->name, s);
1.34      nicm      274:
                    275:        bufferevent_write(cs->write_event, s, strlen(s));
                    276:        bufferevent_write(cs->write_event, "\n", 1);
1.35      nicm      277:
                    278:        bufferevent_enable(cs->write_event, EV_WRITE);
1.34      nicm      279:        free(s);
1.31      nicm      280: }
                    281:
1.35      nicm      282: /* Write a line. */
                    283: void
                    284: control_write(struct client *c, const char *fmt, ...)
                    285: {
                    286:        struct control_state    *cs = c->control_state;
                    287:        struct control_block    *cb;
                    288:        va_list                  ap;
                    289:
                    290:        va_start(ap, fmt);
                    291:
                    292:        if (TAILQ_EMPTY(&cs->all_blocks)) {
                    293:                control_vwrite(c, fmt, ap);
                    294:                va_end(ap);
                    295:                return;
                    296:        }
                    297:
                    298:        cb = xcalloc(1, sizeof *cb);
                    299:        xvasprintf(&cb->line, fmt, ap);
                    300:        TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
1.37      nicm      301:        cb->t = get_timer();
1.35      nicm      302:
                    303:        log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line);
                    304:        bufferevent_enable(cs->write_event, EV_WRITE);
                    305:
                    306:        va_end(ap);
                    307: }
                    308:
1.31      nicm      309: /* Write output from a pane. */
                    310: void
                    311: control_write_output(struct client *c, struct window_pane *wp)
                    312: {
1.34      nicm      313:        struct control_state    *cs = c->control_state;
1.35      nicm      314:        struct control_pane     *cp;
                    315:        struct control_block    *cb;
                    316:        size_t                   new_size;
1.38    ! nicm      317:        uint64_t                 t;
1.31      nicm      318:
                    319:        if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
                    320:                return;
                    321:
1.35      nicm      322:        if (c->flags & CONTROL_IGNORE_FLAGS) {
                    323:                cp = control_get_pane(c, wp);
                    324:                if (cp != NULL)
                    325:                        goto ignore;
1.31      nicm      326:                return;
                    327:        }
1.35      nicm      328:        cp = control_add_pane(c, wp);
1.38    ! nicm      329:        if (cp->flags & (CONTROL_PANE_OFF|CONTROL_PANE_PAUSED))
1.35      nicm      330:                goto ignore;
1.38    ! nicm      331:        if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
        !           332:                cb = TAILQ_FIRST(&cp->blocks);
        !           333:                if (cb != NULL) {
        !           334:                        t = get_timer();
        !           335:                        log_debug("%s: %s: %%%u is %lld behind", __func__,
        !           336:                            c->name, wp->id, (long long)t - cb->t);
        !           337:                        if (cb->t < t - c->pause_age) {
        !           338:                                cp->flags |= CONTROL_PANE_PAUSED;
        !           339:                                control_discard_pane(c, cp);
        !           340:                                control_write(c, "%%pause %%%u", wp->id);
        !           341:                                return;
        !           342:                        }
        !           343:                }
        !           344:        }
1.35      nicm      345:
                    346:        window_pane_get_new_data(wp, &cp->queued, &new_size);
1.31      nicm      347:        if (new_size == 0)
                    348:                return;
1.35      nicm      349:        window_pane_update_used_data(wp, &cp->queued, new_size);
1.31      nicm      350:
1.35      nicm      351:        cb = xcalloc(1, sizeof *cb);
                    352:        cb->size = new_size;
                    353:        TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
1.37      nicm      354:        cb->t = get_timer();
1.35      nicm      355:
                    356:        TAILQ_INSERT_TAIL(&cp->blocks, cb, entry);
                    357:        log_debug("%s: %s: new output block of %zu for %%%u", __func__, c->name,
                    358:            cb->size, wp->id);
                    359:
                    360:        if (!cp->pending_flag) {
                    361:                log_debug("%s: %s: %%%u now pending", __func__, c->name,
                    362:                    wp->id);
                    363:                TAILQ_INSERT_TAIL(&cs->pending_list, cp, pending_entry);
                    364:                cp->pending_flag = 1;
                    365:                cs->pending_count++;
1.31      nicm      366:        }
1.35      nicm      367:        bufferevent_enable(cs->write_event, EV_WRITE);
                    368:        return;
1.31      nicm      369:
1.35      nicm      370: ignore:
                    371:        log_debug("%s: %s: ignoring pane %%%u", __func__, c->name, wp->id);
                    372:        window_pane_update_used_data(wp, &cp->offset, SIZE_MAX);
                    373:        window_pane_update_used_data(wp, &cp->queued, SIZE_MAX);
1.1       nicm      374: }
                    375:
1.34      nicm      376: /* Control client error callback. */
1.18      nicm      377: static enum cmd_retval
1.19      nicm      378: control_error(struct cmdq_item *item, void *data)
1.18      nicm      379: {
1.26      nicm      380:        struct client   *c = cmdq_get_client(item);
1.18      nicm      381:        char            *error = data;
                    382:
1.19      nicm      383:        cmdq_guard(item, "begin", 1);
1.18      nicm      384:        control_write(c, "parse error: %s", error);
1.19      nicm      385:        cmdq_guard(item, "error", 1);
1.18      nicm      386:
                    387:        free(error);
                    388:        return (CMD_RETURN_NORMAL);
                    389: }
                    390:
1.34      nicm      391: /* Control client error callback. */
1.25      nicm      392: static void
1.34      nicm      393: control_error_callback(__unused struct bufferevent *bufev,
                    394:     __unused short what, void *data)
1.1       nicm      395: {
1.34      nicm      396:        struct client   *c = data;
                    397:
                    398:        c->flags |= CLIENT_EXIT;
                    399: }
                    400:
                    401: /* Control client input callback. Read lines and fire commands. */
                    402: static void
                    403: control_read_callback(__unused struct bufferevent *bufev, void *data)
                    404: {
                    405:        struct client           *c = data;
                    406:        struct control_state    *cs = c->control_state;
                    407:        struct evbuffer         *buffer = cs->read_event->input;
1.30      nicm      408:        char                    *line, *error;
1.29      nicm      409:        struct cmdq_state       *state;
1.30      nicm      410:        enum cmd_parse_status    status;
1.1       nicm      411:
                    412:        for (;;) {
1.25      nicm      413:                line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
1.1       nicm      414:                if (line == NULL)
                    415:                        break;
1.35      nicm      416:                log_debug("%s: %s: %s", __func__, c->name, line);
                    417:                if (*line == '\0') { /* empty line detach */
1.24      nicm      418:                        free(line);
1.1       nicm      419:                        c->flags |= CLIENT_EXIT;
                    420:                        break;
                    421:                }
                    422:
1.30      nicm      423:                state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL);
                    424:                status = cmd_parse_and_append(line, NULL, c, state, &error);
                    425:                if (status == CMD_PARSE_ERROR)
                    426:                        cmdq_append(c, cmdq_get_callback(control_error, error));
                    427:                cmdq_free_state(state);
1.1       nicm      428:
1.2       nicm      429:                free(line);
1.1       nicm      430:        }
1.25      nicm      431: }
                    432:
1.35      nicm      433: /* Does this control client have outstanding data to write? */
                    434: int
                    435: control_all_done(struct client *c)
                    436: {
                    437:        struct control_state    *cs = c->control_state;
                    438:
                    439:        if (!TAILQ_EMPTY(&cs->all_blocks))
                    440:                return (0);
                    441:        return (EVBUFFER_LENGTH(cs->write_event->output) == 0);
                    442: }
                    443:
                    444: /* Flush all blocks until output. */
                    445: static void
                    446: control_flush_all_blocks(struct client *c)
                    447: {
                    448:        struct control_state    *cs = c->control_state;
                    449:        struct control_block    *cb, *cb1;
                    450:
                    451:        TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) {
                    452:                if (cb->size != 0)
                    453:                        break;
                    454:                log_debug("%s: %s: flushing line: %s", __func__, c->name,
                    455:                    cb->line);
                    456:
                    457:                bufferevent_write(cs->write_event, cb->line, strlen(cb->line));
                    458:                bufferevent_write(cs->write_event, "\n", 1);
                    459:                control_free_block(cs, cb);
                    460:        }
                    461: }
                    462:
                    463: /* Append data to buffer. */
                    464: static struct evbuffer *
                    465: control_append_data(struct control_pane *cp, struct evbuffer *message,
                    466:     struct window_pane *wp, size_t size)
                    467: {
                    468:        u_char  *new_data;
                    469:        size_t   new_size;
                    470:        u_int    i;
                    471:
                    472:        if (message == NULL) {
                    473:                message = evbuffer_new();
                    474:                if (message == NULL)
                    475:                        fatalx("out of memory");
                    476:                evbuffer_add_printf(message, "%%output %%%u ", wp->id);
                    477:        }
                    478:
                    479:        new_data = window_pane_get_new_data(wp, &cp->offset, &new_size);
                    480:        if (new_size < size)
                    481:                fatalx("not enough data: %zu < %zu", new_size, size);
                    482:        for (i = 0; i < size; i++) {
                    483:                if (new_data[i] < ' ' || new_data[i] == '\\')
                    484:                        evbuffer_add_printf(message, "\\%03o", new_data[i]);
                    485:                else
                    486:                        evbuffer_add_printf(message, "%c", new_data[i]);
                    487:        }
                    488:        window_pane_update_used_data(wp, &cp->offset, size);
                    489:        return (message);
                    490: }
                    491:
                    492: /* Write buffer. */
                    493: static void
                    494: control_write_data(struct client *c, struct evbuffer *message)
                    495: {
                    496:        struct control_state    *cs = c->control_state;
                    497:
                    498:        log_debug("%s: %s: %.*s", __func__, c->name,
                    499:            (int)EVBUFFER_LENGTH(message), EVBUFFER_DATA(message));
                    500:
                    501:        evbuffer_add(message, "\n", 1);
                    502:        bufferevent_write_buffer(cs->write_event, message);
                    503:        evbuffer_free(message);
                    504: }
                    505:
                    506: /* Write output to client. */
                    507: static int
                    508: control_write_pending(struct client *c, struct control_pane *cp, size_t limit)
                    509: {
                    510:        struct control_state    *cs = c->control_state;
                    511:        struct window_pane      *wp = NULL;
                    512:        struct evbuffer         *message = NULL;
                    513:        size_t                   used = 0, size;
                    514:        struct control_block    *cb, *cb1;
                    515:
1.37      nicm      516:        wp = control_window_pane(c, cp->pane);
                    517:        if (wp == NULL) {
1.36      nicm      518:                TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) {
                    519:                        TAILQ_REMOVE(&cp->blocks, cb, entry);
1.35      nicm      520:                        control_free_block(cs, cb);
1.36      nicm      521:                }
1.35      nicm      522:                control_flush_all_blocks(c);
                    523:                return (0);
                    524:        }
                    525:
                    526:        while (used != limit && !TAILQ_EMPTY(&cp->blocks)) {
                    527:                cb = TAILQ_FIRST(&cp->blocks);
                    528:                log_debug("%s: %s: output block %zu for %%%u (used %zu/%zu)",
                    529:                    __func__, c->name, cb->size, cp->pane, used, limit);
                    530:
                    531:                size = cb->size;
                    532:                if (size > limit - used)
                    533:                        size = limit - used;
                    534:                used += size;
                    535:
                    536:                message = control_append_data(cp, message, wp, size);
                    537:
                    538:                cb->size -= size;
                    539:                if (cb->size == 0) {
                    540:                        TAILQ_REMOVE(&cp->blocks, cb, entry);
                    541:                        control_free_block(cs, cb);
                    542:
                    543:                        cb = TAILQ_FIRST(&cs->all_blocks);
                    544:                        if (cb != NULL && cb->size == 0) {
                    545:                                if (wp != NULL && message != NULL) {
                    546:                                        control_write_data(c, message);
                    547:                                        message = NULL;
                    548:                                }
                    549:                                control_flush_all_blocks(c);
                    550:                        }
                    551:                }
                    552:        }
                    553:        if (message != NULL)
                    554:                control_write_data(c, message);
                    555:        return (!TAILQ_EMPTY(&cp->blocks));
                    556: }
                    557:
                    558: /* Control client write callback. */
                    559: static void
                    560: control_write_callback(__unused struct bufferevent *bufev, void *data)
                    561: {
                    562:        struct client           *c = data;
                    563:        struct control_state    *cs = c->control_state;
                    564:        struct control_pane     *cp, *cp1;
                    565:        struct evbuffer         *evb = cs->write_event->output;
                    566:        size_t                   space, limit;
                    567:
                    568:        control_flush_all_blocks(c);
                    569:
                    570:        while (EVBUFFER_LENGTH(evb) < CONTROL_BUFFER_HIGH) {
                    571:                if (cs->pending_count == 0)
                    572:                        break;
                    573:                space = CONTROL_BUFFER_HIGH - EVBUFFER_LENGTH(evb);
                    574:                log_debug("%s: %s: %zu bytes available, %u panes", __func__,
                    575:                    c->name, space, cs->pending_count);
                    576:
                    577:                limit = (space / cs->pending_count / 3); /* 3 bytes for \xxx */
                    578:                if (limit < CONTROL_WRITE_MINIMUM)
                    579:                        limit = CONTROL_WRITE_MINIMUM;
                    580:
                    581:                TAILQ_FOREACH_SAFE(cp, &cs->pending_list, pending_entry, cp1) {
                    582:                        if (EVBUFFER_LENGTH(evb) >= CONTROL_BUFFER_HIGH)
                    583:                                break;
                    584:                        if (control_write_pending(c, cp, limit))
                    585:                                continue;
                    586:                        TAILQ_REMOVE(&cs->pending_list, cp, pending_entry);
                    587:                        cp->pending_flag = 0;
                    588:                        cs->pending_count--;
                    589:                }
                    590:        }
                    591:        if (EVBUFFER_LENGTH(evb) == 0)
                    592:                bufferevent_disable(cs->write_event, EV_WRITE);
                    593: }
                    594:
1.32      nicm      595: /* Initialize for control mode. */
1.25      nicm      596: void
                    597: control_start(struct client *c)
                    598: {
1.33      nicm      599:        struct control_state    *cs;
                    600:
1.34      nicm      601:        if (c->flags & CLIENT_CONTROLCONTROL) {
                    602:                close(c->out_fd);
                    603:                c->out_fd = -1;
                    604:        } else
                    605:                setblocking(c->out_fd, 0);
                    606:        setblocking(c->fd, 0);
                    607:
1.33      nicm      608:        cs = c->control_state = xcalloc(1, sizeof *cs);
1.35      nicm      609:        RB_INIT(&cs->panes);
                    610:        TAILQ_INIT(&cs->pending_list);
                    611:        TAILQ_INIT(&cs->all_blocks);
1.33      nicm      612:
1.35      nicm      613:        cs->read_event = bufferevent_new(c->fd, control_read_callback,
                    614:            control_write_callback, control_error_callback, c);
1.34      nicm      615:        bufferevent_enable(cs->read_event, EV_READ);
1.25      nicm      616:
                    617:        if (c->flags & CLIENT_CONTROLCONTROL)
1.34      nicm      618:                cs->write_event = cs->read_event;
                    619:        else {
1.35      nicm      620:                cs->write_event = bufferevent_new(c->out_fd, NULL,
                    621:                    control_write_callback, control_error_callback, c);
                    622:        }
                    623:        bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
                    624:            0);
                    625:
                    626:        if (c->flags & CLIENT_CONTROLCONTROL) {
                    627:                bufferevent_write(cs->write_event, "\033P1000p", 7);
                    628:                bufferevent_enable(cs->write_event, EV_WRITE);
1.34      nicm      629:        }
1.35      nicm      630: }
                    631:
1.38    ! nicm      632: /* Discard all output for a client. */
1.35      nicm      633: void
1.38    ! nicm      634: control_discard(struct client *c)
1.35      nicm      635: {
                    636:        struct control_state    *cs = c->control_state;
                    637:        struct control_pane     *cp;
1.34      nicm      638:
1.38    ! nicm      639:        RB_FOREACH(cp, control_panes, &cs->panes)
        !           640:                control_discard_pane(c, cp);
1.33      nicm      641: }
                    642:
                    643: /* Stop control mode. */
                    644: void
                    645: control_stop(struct client *c)
                    646: {
                    647:        struct control_state    *cs = c->control_state;
1.35      nicm      648:        struct control_block    *cb, *cb1;
1.34      nicm      649:
                    650:        if (~c->flags & CLIENT_CONTROLCONTROL)
                    651:                bufferevent_free(cs->write_event);
                    652:        bufferevent_free(cs->read_event);
1.33      nicm      653:
1.35      nicm      654:        TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
                    655:                control_free_block(cs, cb);
                    656:        control_reset_offsets(c);
                    657:
1.33      nicm      658:        free(cs);
1.1       nicm      659: }