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

1.44    ! nicm        1: /* $OpenBSD: control.c,v 1.43 2020/06/18 08:34:22 nicm Exp $ */
1.1       nicm        2:
                      3: /*
1.17      nicm        4:  * Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1       nicm        5:  * Copyright (c) 2012 George Nachman <tmux@georgester.com>
                      6:  *
                      7:  * Permission to use, copy, modify, and distribute this software for any
                      8:  * purpose with or without fee is hereby granted, provided that the above
                      9:  * copyright notice and this permission notice appear in all copies.
                     10:  *
                     11:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     12:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     13:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     14:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     15:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
                     16:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
                     17:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     18:  */
                     19:
                     20: #include <sys/types.h>
                     21:
                     22: #include <event.h>
1.2       nicm       23: #include <stdlib.h>
1.1       nicm       24: #include <string.h>
1.10      nicm       25: #include <time.h>
1.34      nicm       26: #include <unistd.h>
1.1       nicm       27:
                     28: #include "tmux.h"
                     29:
1.35      nicm       30: /*
                     31:  * Block of data to output. Each client has one "all" queue of blocks and
                     32:  * another queue for each pane (in struct client_offset). %output blocks are
                     33:  * added to both queues and other output lines (notifications) added only to
                     34:  * the client queue.
                     35:  *
                     36:  * When a client becomes writeable, data from blocks on the pane queue are sent
                     37:  * up to the maximum size (CLIENT_BUFFER_HIGH). If a block is entirely written,
                     38:  * it is removed from both pane and client queues and if this means non-%output
                     39:  * blocks are now at the head of the client queue, they are written.
                     40:  *
                     41:  * This means a %output block holds up any subsequent non-%output blocks until
                     42:  * it is written which enforces ordering even if the client cannot accept the
                     43:  * entire block in one go.
                     44:  */
                     45: struct control_block {
                     46:        size_t                           size;
                     47:        char                            *line;
1.37      nicm       48:        uint64_t                         t;
1.35      nicm       49:
                     50:        TAILQ_ENTRY(control_block)       entry;
                     51:        TAILQ_ENTRY(control_block)       all_entry;
1.36      nicm       52: };
1.35      nicm       53:
                     54: /* Control client pane. */
                     55: struct control_pane {
                     56:        u_int                            pane;
                     57:
                     58:        /*
                     59:         * Offsets into the pane data. The first (offset) is the data we have
                     60:         * written; the second (queued) the data we have queued (pointed to by
                     61:         * a block).
                     62:         */
                     63:        struct window_pane_offset        offset;
                     64:        struct window_pane_offset        queued;
                     65:
                     66:        int                              flags;
                     67: #define CONTROL_PANE_OFF 0x1
1.38      nicm       68: #define CONTROL_PANE_PAUSED 0x2
1.32      nicm       69:
1.35      nicm       70:        int                              pending_flag;
                     71:        TAILQ_ENTRY(control_pane)        pending_entry;
                     72:
                     73:        TAILQ_HEAD(, control_block)      blocks;
                     74:
                     75:        RB_ENTRY(control_pane)           entry;
1.32      nicm       76: };
1.35      nicm       77: RB_HEAD(control_panes, control_pane);
1.32      nicm       78:
1.44    ! nicm       79: /* Subscription pane. */
        !            80: struct control_sub_pane {
        !            81:        u_int                            pane;
        !            82:        u_int                            idx;
        !            83:        char                            *last;
        !            84:
        !            85:        RB_ENTRY(control_sub_pane)       entry;
        !            86: };
        !            87: RB_HEAD(control_sub_panes, control_sub_pane);
        !            88:
        !            89: /* Subscription window. */
        !            90: struct control_sub_window {
        !            91:        u_int                            window;
        !            92:        u_int                            idx;
        !            93:        char                            *last;
        !            94:
        !            95:        RB_ENTRY(control_sub_window)     entry;
        !            96: };
        !            97: RB_HEAD(control_sub_windows, control_sub_window);
        !            98:
        !            99: /* Control client subscription. */
        !           100: struct control_sub {
        !           101:        char                            *name;
        !           102:        char                            *format;
        !           103:
        !           104:        enum control_sub_type            type;
        !           105:        u_int                            id;
        !           106:
        !           107:        char                            *last;
        !           108:        struct control_sub_panes         panes;
        !           109:        struct control_sub_windows       windows;
        !           110:
        !           111:        RB_ENTRY(control_sub)            entry;
        !           112: };
        !           113: RB_HEAD(control_subs, control_sub);
        !           114:
1.34      nicm      115: /* Control client state. */
1.33      nicm      116: struct control_state {
1.35      nicm      117:        struct control_panes             panes;
                    118:
                    119:        TAILQ_HEAD(, control_pane)       pending_list;
                    120:        u_int                            pending_count;
                    121:
                    122:        TAILQ_HEAD(, control_block)      all_blocks;
1.34      nicm      123:
1.35      nicm      124:        struct bufferevent              *read_event;
                    125:        struct bufferevent              *write_event;
1.44    ! nicm      126:
        !           127:        struct control_subs              subs;
        !           128:        struct event                     subs_timer;
1.33      nicm      129: };
                    130:
1.40      nicm      131: /* Low and high watermarks. */
1.35      nicm      132: #define CONTROL_BUFFER_LOW 512
                    133: #define CONTROL_BUFFER_HIGH 8192
                    134:
                    135: /* Minimum to write to each client. */
                    136: #define CONTROL_WRITE_MINIMUM 32
                    137:
1.40      nicm      138: /* Maximum age for clients that are not using pause mode. */
                    139: #define CONTROL_MAXIMUM_AGE 300000
                    140:
1.35      nicm      141: /* Flags to ignore client. */
                    142: #define CONTROL_IGNORE_FLAGS \
                    143:        (CLIENT_CONTROL_NOOUTPUT| \
                    144:         CLIENT_UNATTACHEDFLAGS)
                    145:
                    146: /* Compare client panes. */
1.32      nicm      147: static int
1.35      nicm      148: control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
1.32      nicm      149: {
1.35      nicm      150:        if (cp1->pane < cp2->pane)
1.32      nicm      151:                return (-1);
1.35      nicm      152:        if (cp1->pane > cp2->pane)
1.32      nicm      153:                return (1);
                    154:        return (0);
                    155: }
1.35      nicm      156: RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
                    157:
1.44    ! nicm      158: /* Compare client subs. */
        !           159: static int
        !           160: control_sub_cmp(struct control_sub *csub1, struct control_sub *csub2)
        !           161: {
        !           162:        return (strcmp(csub1->name, csub2->name));
        !           163: }
        !           164: RB_GENERATE_STATIC(control_subs, control_sub, entry, control_sub_cmp);
        !           165:
        !           166: /* Compare client subscription panes. */
        !           167: static int
        !           168: control_sub_pane_cmp(struct control_sub_pane *csp1,
        !           169:     struct control_sub_pane *csp2)
        !           170: {
        !           171:        if (csp1->pane < csp2->pane)
        !           172:                return (-1);
        !           173:        if (csp1->pane > csp2->pane)
        !           174:                return (1);
        !           175:        if (csp1->idx < csp2->idx)
        !           176:                return (-1);
        !           177:        if (csp1->idx > csp2->idx)
        !           178:                return (1);
        !           179:        return (0);
        !           180: }
        !           181: RB_GENERATE_STATIC(control_sub_panes, control_sub_pane, entry,
        !           182:     control_sub_pane_cmp);
        !           183:
        !           184: /* Compare client subscription windows. */
        !           185: static int
        !           186: control_sub_window_cmp(struct control_sub_window *csw1,
        !           187:     struct control_sub_window *csw2)
        !           188: {
        !           189:        if (csw1->window < csw2->window)
        !           190:                return (-1);
        !           191:        if (csw1->window > csw2->window)
        !           192:                return (1);
        !           193:        if (csw1->idx < csw2->idx)
        !           194:                return (-1);
        !           195:        if (csw1->idx > csw2->idx)
        !           196:                return (1);
        !           197:        return (0);
        !           198: }
        !           199: RB_GENERATE_STATIC(control_sub_windows, control_sub_window, entry,
        !           200:     control_sub_window_cmp);
        !           201:
        !           202: /* Free a subscription. */
        !           203: static void
        !           204: control_free_sub(struct control_state *cs, struct control_sub *csub)
        !           205: {
        !           206:        struct control_sub_pane         *csp, *csp1;
        !           207:        struct control_sub_window       *csw, *csw1;
        !           208:
        !           209:        RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) {
        !           210:                RB_REMOVE(control_sub_panes, &csub->panes, csp);
        !           211:                free(csp);
        !           212:        }
        !           213:        RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) {
        !           214:                RB_REMOVE(control_sub_windows, &csub->windows, csw);
        !           215:                free(csw);
        !           216:        }
        !           217:        free(csub->last);
        !           218:
        !           219:        RB_REMOVE(control_subs, &cs->subs, csub);
        !           220:        free(csub->name);
        !           221:        free(csub->format);
        !           222:        free(csub);
        !           223: }
        !           224:
1.35      nicm      225: /* Free a block. */
                    226: static void
                    227: control_free_block(struct control_state *cs, struct control_block *cb)
                    228: {
                    229:        free(cb->line);
                    230:        TAILQ_REMOVE(&cs->all_blocks, cb, all_entry);
                    231:        free(cb);
                    232: }
1.32      nicm      233:
                    234: /* Get pane offsets for this client. */
1.35      nicm      235: static struct control_pane *
                    236: control_get_pane(struct client *c, struct window_pane *wp)
1.32      nicm      237: {
1.33      nicm      238:        struct control_state    *cs = c->control_state;
1.35      nicm      239:        struct control_pane      cp = { .pane = wp->id };
1.32      nicm      240:
1.35      nicm      241:        return (RB_FIND(control_panes, &cs->panes, &cp));
1.32      nicm      242: }
                    243:
                    244: /* Add pane offsets for this client. */
1.35      nicm      245: static struct control_pane *
                    246: control_add_pane(struct client *c, struct window_pane *wp)
1.32      nicm      247: {
1.33      nicm      248:        struct control_state    *cs = c->control_state;
1.35      nicm      249:        struct control_pane     *cp;
                    250:
                    251:        cp = control_get_pane(c, wp);
                    252:        if (cp != NULL)
                    253:                return (cp);
1.32      nicm      254:
1.35      nicm      255:        cp = xcalloc(1, sizeof *cp);
                    256:        cp->pane = wp->id;
                    257:        RB_INSERT(control_panes, &cs->panes, cp);
1.32      nicm      258:
1.35      nicm      259:        memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
                    260:        memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
                    261:        TAILQ_INIT(&cp->blocks);
                    262:
                    263:        return (cp);
1.32      nicm      264: }
                    265:
1.38      nicm      266: /* Discard output for a pane. */
                    267: static void
                    268: control_discard_pane(struct client *c, struct control_pane *cp)
                    269: {
                    270:        struct control_state    *cs = c->control_state;
                    271:        struct control_block    *cb, *cb1;
                    272:
                    273:        TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) {
                    274:                TAILQ_REMOVE(&cp->blocks, cb, entry);
                    275:                control_free_block(cs, cb);
                    276:        }
                    277: }
                    278:
1.37      nicm      279: /* Get actual pane for this client. */
                    280: static struct window_pane *
                    281: control_window_pane(struct client *c, u_int pane)
                    282: {
                    283:        struct window_pane      *wp;
                    284:
                    285:        if (c->session == NULL)
                    286:                return (NULL);
                    287:        if ((wp = window_pane_find_by_id(pane)) == NULL)
                    288:                return (NULL);
                    289:        if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
                    290:                return (NULL);
                    291:        return (wp);
                    292: }
                    293:
1.35      nicm      294: /* Reset control offsets. */
1.32      nicm      295: void
1.35      nicm      296: control_reset_offsets(struct client *c)
1.32      nicm      297: {
1.33      nicm      298:        struct control_state    *cs = c->control_state;
1.35      nicm      299:        struct control_pane     *cp, *cp1;
1.32      nicm      300:
1.35      nicm      301:        RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) {
                    302:                RB_REMOVE(control_panes, &cs->panes, cp);
                    303:                free(cp);
1.32      nicm      304:        }
1.35      nicm      305:
                    306:        TAILQ_INIT(&cs->pending_list);
                    307:        cs->pending_count = 0;
1.32      nicm      308: }
                    309:
                    310: /* Get offsets for client. */
                    311: struct window_pane_offset *
                    312: control_pane_offset(struct client *c, struct window_pane *wp, int *off)
                    313: {
1.35      nicm      314:        struct control_state    *cs = c->control_state;
                    315:        struct control_pane     *cp;
1.32      nicm      316:
                    317:        if (c->flags & CLIENT_CONTROL_NOOUTPUT) {
                    318:                *off = 0;
                    319:                return (NULL);
                    320:        }
                    321:
1.35      nicm      322:        cp = control_get_pane(c, wp);
1.38      nicm      323:        if (cp == NULL || (cp->flags & CONTROL_PANE_PAUSED)) {
1.32      nicm      324:                *off = 0;
                    325:                return (NULL);
                    326:        }
1.35      nicm      327:        if (cp->flags & CONTROL_PANE_OFF) {
1.32      nicm      328:                *off = 1;
                    329:                return (NULL);
                    330:        }
1.35      nicm      331:        *off = (EVBUFFER_LENGTH(cs->write_event->output) >= CONTROL_BUFFER_LOW);
                    332:        return (&cp->offset);
1.32      nicm      333: }
                    334:
                    335: /* Set pane as on. */
                    336: void
                    337: control_set_pane_on(struct client *c, struct window_pane *wp)
                    338: {
1.35      nicm      339:        struct control_pane     *cp;
1.32      nicm      340:
1.35      nicm      341:        cp = control_get_pane(c, wp);
1.38      nicm      342:        if (cp != NULL && (cp->flags & CONTROL_PANE_OFF)) {
1.35      nicm      343:                cp->flags &= ~CONTROL_PANE_OFF;
                    344:                memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
                    345:                memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
1.32      nicm      346:        }
                    347: }
                    348:
                    349: /* Set pane as off. */
                    350: void
                    351: control_set_pane_off(struct client *c, struct window_pane *wp)
                    352: {
1.35      nicm      353:        struct control_pane     *cp;
1.32      nicm      354:
1.35      nicm      355:        cp = control_add_pane(c, wp);
                    356:        cp->flags |= CONTROL_PANE_OFF;
1.32      nicm      357: }
                    358:
1.38      nicm      359: /* Continue a paused pane. */
                    360: void
                    361: control_continue_pane(struct client *c, struct window_pane *wp)
                    362: {
                    363:        struct control_pane     *cp;
                    364:
                    365:        cp = control_get_pane(c, wp);
                    366:        if (cp != NULL && (cp->flags & CONTROL_PANE_PAUSED)) {
                    367:                cp->flags &= ~CONTROL_PANE_PAUSED;
                    368:                memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
                    369:                memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
                    370:                control_write(c, "%%continue %%%u", wp->id);
1.41      nicm      371:        }
                    372: }
                    373:
                    374: /* Pause a pane. */
                    375: void
                    376: control_pause_pane(struct client *c, struct window_pane *wp)
                    377: {
                    378:        struct control_pane     *cp;
                    379:
                    380:        cp = control_add_pane(c, wp);
                    381:        if (~cp->flags & CONTROL_PANE_PAUSED) {
                    382:                cp->flags |= CONTROL_PANE_PAUSED;
                    383:                control_discard_pane(c, cp);
                    384:                control_write(c, "%%pause %%%u", wp->id);
1.38      nicm      385:        }
                    386: }
                    387:
1.1       nicm      388: /* Write a line. */
1.35      nicm      389: static void
                    390: control_vwrite(struct client *c, const char *fmt, va_list ap)
1.1       nicm      391: {
1.34      nicm      392:        struct control_state    *cs = c->control_state;
                    393:        char                    *s;
1.1       nicm      394:
1.34      nicm      395:        xvasprintf(&s, fmt, ap);
1.35      nicm      396:        log_debug("%s: %s: writing line: %s", __func__, c->name, s);
1.34      nicm      397:
                    398:        bufferevent_write(cs->write_event, s, strlen(s));
                    399:        bufferevent_write(cs->write_event, "\n", 1);
1.35      nicm      400:
                    401:        bufferevent_enable(cs->write_event, EV_WRITE);
1.34      nicm      402:        free(s);
1.31      nicm      403: }
                    404:
1.35      nicm      405: /* Write a line. */
                    406: void
                    407: control_write(struct client *c, const char *fmt, ...)
                    408: {
                    409:        struct control_state    *cs = c->control_state;
                    410:        struct control_block    *cb;
                    411:        va_list                  ap;
                    412:
                    413:        va_start(ap, fmt);
                    414:
                    415:        if (TAILQ_EMPTY(&cs->all_blocks)) {
                    416:                control_vwrite(c, fmt, ap);
                    417:                va_end(ap);
                    418:                return;
                    419:        }
                    420:
                    421:        cb = xcalloc(1, sizeof *cb);
                    422:        xvasprintf(&cb->line, fmt, ap);
                    423:        TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
1.37      nicm      424:        cb->t = get_timer();
1.35      nicm      425:
                    426:        log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line);
                    427:        bufferevent_enable(cs->write_event, EV_WRITE);
                    428:
                    429:        va_end(ap);
                    430: }
                    431:
1.40      nicm      432: /* Check age for this pane. */
                    433: static int
                    434: control_check_age(struct client *c, struct window_pane *wp,
                    435:     struct control_pane *cp)
                    436: {
                    437:        struct control_block    *cb;
                    438:        uint64_t                 t, age;
                    439:
                    440:        cb = TAILQ_FIRST(&cp->blocks);
                    441:        if (cb == NULL)
                    442:                return (0);
                    443:        t = get_timer();
                    444:        if (cb->t >= t)
                    445:                return (0);
                    446:
                    447:        age = t - cb->t;
                    448:        log_debug("%s: %s: %%%u is %llu behind", __func__, c->name, wp->id,
                    449:            (unsigned long long)age);
                    450:
                    451:        if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
                    452:                if (age < c->pause_age)
                    453:                        return (0);
                    454:                cp->flags |= CONTROL_PANE_PAUSED;
                    455:                control_discard_pane(c, cp);
                    456:                control_write(c, "%%pause %%%u", wp->id);
                    457:        } else {
                    458:                if (age < CONTROL_MAXIMUM_AGE)
                    459:                        return (0);
                    460:                c->exit_message = xstrdup("too far behind");
                    461:                c->flags |= CLIENT_EXIT;
                    462:                control_discard(c);
                    463:        }
                    464:        return (1);
                    465: }
                    466:
1.31      nicm      467: /* Write output from a pane. */
                    468: void
                    469: control_write_output(struct client *c, struct window_pane *wp)
                    470: {
1.34      nicm      471:        struct control_state    *cs = c->control_state;
1.35      nicm      472:        struct control_pane     *cp;
                    473:        struct control_block    *cb;
                    474:        size_t                   new_size;
1.31      nicm      475:
                    476:        if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
                    477:                return;
                    478:
1.35      nicm      479:        if (c->flags & CONTROL_IGNORE_FLAGS) {
                    480:                cp = control_get_pane(c, wp);
                    481:                if (cp != NULL)
                    482:                        goto ignore;
1.31      nicm      483:                return;
                    484:        }
1.35      nicm      485:        cp = control_add_pane(c, wp);
1.38      nicm      486:        if (cp->flags & (CONTROL_PANE_OFF|CONTROL_PANE_PAUSED))
1.35      nicm      487:                goto ignore;
1.40      nicm      488:        if (control_check_age(c, wp, cp))
                    489:                return;
1.35      nicm      490:
                    491:        window_pane_get_new_data(wp, &cp->queued, &new_size);
1.31      nicm      492:        if (new_size == 0)
                    493:                return;
1.35      nicm      494:        window_pane_update_used_data(wp, &cp->queued, new_size);
1.31      nicm      495:
1.35      nicm      496:        cb = xcalloc(1, sizeof *cb);
                    497:        cb->size = new_size;
                    498:        TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
1.37      nicm      499:        cb->t = get_timer();
1.35      nicm      500:
                    501:        TAILQ_INSERT_TAIL(&cp->blocks, cb, entry);
                    502:        log_debug("%s: %s: new output block of %zu for %%%u", __func__, c->name,
                    503:            cb->size, wp->id);
                    504:
                    505:        if (!cp->pending_flag) {
                    506:                log_debug("%s: %s: %%%u now pending", __func__, c->name,
                    507:                    wp->id);
                    508:                TAILQ_INSERT_TAIL(&cs->pending_list, cp, pending_entry);
                    509:                cp->pending_flag = 1;
                    510:                cs->pending_count++;
1.31      nicm      511:        }
1.35      nicm      512:        bufferevent_enable(cs->write_event, EV_WRITE);
                    513:        return;
1.31      nicm      514:
1.35      nicm      515: ignore:
                    516:        log_debug("%s: %s: ignoring pane %%%u", __func__, c->name, wp->id);
                    517:        window_pane_update_used_data(wp, &cp->offset, SIZE_MAX);
                    518:        window_pane_update_used_data(wp, &cp->queued, SIZE_MAX);
1.1       nicm      519: }
                    520:
1.34      nicm      521: /* Control client error callback. */
1.18      nicm      522: static enum cmd_retval
1.19      nicm      523: control_error(struct cmdq_item *item, void *data)
1.18      nicm      524: {
1.26      nicm      525:        struct client   *c = cmdq_get_client(item);
1.18      nicm      526:        char            *error = data;
                    527:
1.19      nicm      528:        cmdq_guard(item, "begin", 1);
1.18      nicm      529:        control_write(c, "parse error: %s", error);
1.19      nicm      530:        cmdq_guard(item, "error", 1);
1.18      nicm      531:
                    532:        free(error);
                    533:        return (CMD_RETURN_NORMAL);
                    534: }
                    535:
1.34      nicm      536: /* Control client error callback. */
1.25      nicm      537: static void
1.34      nicm      538: control_error_callback(__unused struct bufferevent *bufev,
                    539:     __unused short what, void *data)
1.1       nicm      540: {
1.34      nicm      541:        struct client   *c = data;
                    542:
                    543:        c->flags |= CLIENT_EXIT;
                    544: }
                    545:
                    546: /* Control client input callback. Read lines and fire commands. */
                    547: static void
                    548: control_read_callback(__unused struct bufferevent *bufev, void *data)
                    549: {
                    550:        struct client           *c = data;
                    551:        struct control_state    *cs = c->control_state;
                    552:        struct evbuffer         *buffer = cs->read_event->input;
1.30      nicm      553:        char                    *line, *error;
1.29      nicm      554:        struct cmdq_state       *state;
1.30      nicm      555:        enum cmd_parse_status    status;
1.1       nicm      556:
                    557:        for (;;) {
1.25      nicm      558:                line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
1.1       nicm      559:                if (line == NULL)
                    560:                        break;
1.35      nicm      561:                log_debug("%s: %s: %s", __func__, c->name, line);
                    562:                if (*line == '\0') { /* empty line detach */
1.24      nicm      563:                        free(line);
1.1       nicm      564:                        c->flags |= CLIENT_EXIT;
                    565:                        break;
                    566:                }
                    567:
1.30      nicm      568:                state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL);
                    569:                status = cmd_parse_and_append(line, NULL, c, state, &error);
                    570:                if (status == CMD_PARSE_ERROR)
                    571:                        cmdq_append(c, cmdq_get_callback(control_error, error));
                    572:                cmdq_free_state(state);
1.1       nicm      573:
1.2       nicm      574:                free(line);
1.1       nicm      575:        }
1.25      nicm      576: }
                    577:
1.35      nicm      578: /* Does this control client have outstanding data to write? */
                    579: int
                    580: control_all_done(struct client *c)
                    581: {
                    582:        struct control_state    *cs = c->control_state;
                    583:
                    584:        if (!TAILQ_EMPTY(&cs->all_blocks))
                    585:                return (0);
                    586:        return (EVBUFFER_LENGTH(cs->write_event->output) == 0);
                    587: }
                    588:
                    589: /* Flush all blocks until output. */
                    590: static void
                    591: control_flush_all_blocks(struct client *c)
                    592: {
                    593:        struct control_state    *cs = c->control_state;
                    594:        struct control_block    *cb, *cb1;
                    595:
                    596:        TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) {
                    597:                if (cb->size != 0)
                    598:                        break;
                    599:                log_debug("%s: %s: flushing line: %s", __func__, c->name,
                    600:                    cb->line);
                    601:
                    602:                bufferevent_write(cs->write_event, cb->line, strlen(cb->line));
                    603:                bufferevent_write(cs->write_event, "\n", 1);
                    604:                control_free_block(cs, cb);
                    605:        }
                    606: }
                    607:
                    608: /* Append data to buffer. */
                    609: static struct evbuffer *
1.39      nicm      610: control_append_data(struct client *c, struct control_pane *cp, uint64_t age,
                    611:     struct evbuffer *message, struct window_pane *wp, size_t size)
1.35      nicm      612: {
                    613:        u_char  *new_data;
                    614:        size_t   new_size;
                    615:        u_int    i;
                    616:
                    617:        if (message == NULL) {
                    618:                message = evbuffer_new();
                    619:                if (message == NULL)
                    620:                        fatalx("out of memory");
1.39      nicm      621:                if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
                    622:                        evbuffer_add_printf(message,
                    623:                            "%%extended-output %%%u %llu : ", wp->id,
                    624:                            (unsigned long long)age);
                    625:                } else
                    626:                        evbuffer_add_printf(message, "%%output %%%u ", wp->id);
1.35      nicm      627:        }
                    628:
                    629:        new_data = window_pane_get_new_data(wp, &cp->offset, &new_size);
                    630:        if (new_size < size)
                    631:                fatalx("not enough data: %zu < %zu", new_size, size);
                    632:        for (i = 0; i < size; i++) {
                    633:                if (new_data[i] < ' ' || new_data[i] == '\\')
                    634:                        evbuffer_add_printf(message, "\\%03o", new_data[i]);
                    635:                else
                    636:                        evbuffer_add_printf(message, "%c", new_data[i]);
                    637:        }
                    638:        window_pane_update_used_data(wp, &cp->offset, size);
                    639:        return (message);
                    640: }
                    641:
                    642: /* Write buffer. */
                    643: static void
                    644: control_write_data(struct client *c, struct evbuffer *message)
                    645: {
                    646:        struct control_state    *cs = c->control_state;
                    647:
                    648:        log_debug("%s: %s: %.*s", __func__, c->name,
                    649:            (int)EVBUFFER_LENGTH(message), EVBUFFER_DATA(message));
                    650:
                    651:        evbuffer_add(message, "\n", 1);
                    652:        bufferevent_write_buffer(cs->write_event, message);
                    653:        evbuffer_free(message);
                    654: }
                    655:
                    656: /* Write output to client. */
                    657: static int
                    658: control_write_pending(struct client *c, struct control_pane *cp, size_t limit)
                    659: {
                    660:        struct control_state    *cs = c->control_state;
                    661:        struct window_pane      *wp = NULL;
                    662:        struct evbuffer         *message = NULL;
                    663:        size_t                   used = 0, size;
                    664:        struct control_block    *cb, *cb1;
1.39      nicm      665:        uint64_t                 age, t = get_timer();
1.35      nicm      666:
1.37      nicm      667:        wp = control_window_pane(c, cp->pane);
                    668:        if (wp == NULL) {
1.36      nicm      669:                TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) {
                    670:                        TAILQ_REMOVE(&cp->blocks, cb, entry);
1.35      nicm      671:                        control_free_block(cs, cb);
1.36      nicm      672:                }
1.35      nicm      673:                control_flush_all_blocks(c);
                    674:                return (0);
                    675:        }
                    676:
                    677:        while (used != limit && !TAILQ_EMPTY(&cp->blocks)) {
1.42      nicm      678:                if (control_check_age(c, wp, cp)) {
                    679:                        if (message != NULL)
                    680:                                evbuffer_free(message);
                    681:                        message = NULL;
                    682:                        break;
                    683:                }
                    684:
1.35      nicm      685:                cb = TAILQ_FIRST(&cp->blocks);
1.39      nicm      686:                if (cb->t < t)
                    687:                        age = t - cb->t;
                    688:                else
                    689:                        age = 0;
                    690:                log_debug("%s: %s: output block %zu (age %llu) for %%%u "
                    691:                    "(used %zu/%zu)", __func__, c->name, cb->size, age,
                    692:                    cp->pane, used, limit);
1.35      nicm      693:
                    694:                size = cb->size;
                    695:                if (size > limit - used)
                    696:                        size = limit - used;
                    697:                used += size;
                    698:
1.39      nicm      699:                message = control_append_data(c, cp, age, message, wp, size);
1.35      nicm      700:
                    701:                cb->size -= size;
                    702:                if (cb->size == 0) {
                    703:                        TAILQ_REMOVE(&cp->blocks, cb, entry);
                    704:                        control_free_block(cs, cb);
                    705:
                    706:                        cb = TAILQ_FIRST(&cs->all_blocks);
                    707:                        if (cb != NULL && cb->size == 0) {
                    708:                                if (wp != NULL && message != NULL) {
                    709:                                        control_write_data(c, message);
                    710:                                        message = NULL;
                    711:                                }
                    712:                                control_flush_all_blocks(c);
                    713:                        }
                    714:                }
                    715:        }
                    716:        if (message != NULL)
                    717:                control_write_data(c, message);
                    718:        return (!TAILQ_EMPTY(&cp->blocks));
                    719: }
                    720:
                    721: /* Control client write callback. */
                    722: static void
                    723: control_write_callback(__unused struct bufferevent *bufev, void *data)
                    724: {
                    725:        struct client           *c = data;
                    726:        struct control_state    *cs = c->control_state;
                    727:        struct control_pane     *cp, *cp1;
                    728:        struct evbuffer         *evb = cs->write_event->output;
                    729:        size_t                   space, limit;
                    730:
                    731:        control_flush_all_blocks(c);
                    732:
                    733:        while (EVBUFFER_LENGTH(evb) < CONTROL_BUFFER_HIGH) {
                    734:                if (cs->pending_count == 0)
                    735:                        break;
                    736:                space = CONTROL_BUFFER_HIGH - EVBUFFER_LENGTH(evb);
                    737:                log_debug("%s: %s: %zu bytes available, %u panes", __func__,
                    738:                    c->name, space, cs->pending_count);
                    739:
                    740:                limit = (space / cs->pending_count / 3); /* 3 bytes for \xxx */
                    741:                if (limit < CONTROL_WRITE_MINIMUM)
                    742:                        limit = CONTROL_WRITE_MINIMUM;
                    743:
                    744:                TAILQ_FOREACH_SAFE(cp, &cs->pending_list, pending_entry, cp1) {
                    745:                        if (EVBUFFER_LENGTH(evb) >= CONTROL_BUFFER_HIGH)
                    746:                                break;
                    747:                        if (control_write_pending(c, cp, limit))
                    748:                                continue;
                    749:                        TAILQ_REMOVE(&cs->pending_list, cp, pending_entry);
                    750:                        cp->pending_flag = 0;
                    751:                        cs->pending_count--;
                    752:                }
                    753:        }
                    754:        if (EVBUFFER_LENGTH(evb) == 0)
                    755:                bufferevent_disable(cs->write_event, EV_WRITE);
                    756: }
                    757:
1.32      nicm      758: /* Initialize for control mode. */
1.25      nicm      759: void
                    760: control_start(struct client *c)
                    761: {
1.33      nicm      762:        struct control_state    *cs;
                    763:
1.34      nicm      764:        if (c->flags & CLIENT_CONTROLCONTROL) {
                    765:                close(c->out_fd);
                    766:                c->out_fd = -1;
                    767:        } else
                    768:                setblocking(c->out_fd, 0);
                    769:        setblocking(c->fd, 0);
                    770:
1.33      nicm      771:        cs = c->control_state = xcalloc(1, sizeof *cs);
1.35      nicm      772:        RB_INIT(&cs->panes);
                    773:        TAILQ_INIT(&cs->pending_list);
                    774:        TAILQ_INIT(&cs->all_blocks);
1.44    ! nicm      775:        RB_INIT(&cs->subs);
1.33      nicm      776:
1.35      nicm      777:        cs->read_event = bufferevent_new(c->fd, control_read_callback,
                    778:            control_write_callback, control_error_callback, c);
1.34      nicm      779:        bufferevent_enable(cs->read_event, EV_READ);
1.25      nicm      780:
                    781:        if (c->flags & CLIENT_CONTROLCONTROL)
1.34      nicm      782:                cs->write_event = cs->read_event;
                    783:        else {
1.35      nicm      784:                cs->write_event = bufferevent_new(c->out_fd, NULL,
                    785:                    control_write_callback, control_error_callback, c);
                    786:        }
                    787:        bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
                    788:            0);
                    789:
                    790:        if (c->flags & CLIENT_CONTROLCONTROL) {
                    791:                bufferevent_write(cs->write_event, "\033P1000p", 7);
                    792:                bufferevent_enable(cs->write_event, EV_WRITE);
1.34      nicm      793:        }
1.35      nicm      794: }
                    795:
1.38      nicm      796: /* Discard all output for a client. */
1.35      nicm      797: void
1.38      nicm      798: control_discard(struct client *c)
1.35      nicm      799: {
                    800:        struct control_state    *cs = c->control_state;
                    801:        struct control_pane     *cp;
1.34      nicm      802:
1.38      nicm      803:        RB_FOREACH(cp, control_panes, &cs->panes)
                    804:                control_discard_pane(c, cp);
1.43      nicm      805:        bufferevent_disable(cs->read_event, EV_READ);
1.33      nicm      806: }
                    807:
                    808: /* Stop control mode. */
                    809: void
                    810: control_stop(struct client *c)
                    811: {
                    812:        struct control_state    *cs = c->control_state;
1.35      nicm      813:        struct control_block    *cb, *cb1;
1.44    ! nicm      814:        struct control_sub      *csub, *csub1;
1.34      nicm      815:
                    816:        if (~c->flags & CLIENT_CONTROLCONTROL)
                    817:                bufferevent_free(cs->write_event);
                    818:        bufferevent_free(cs->read_event);
1.33      nicm      819:
1.44    ! nicm      820:        RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1)
        !           821:                control_free_sub(cs, csub);
        !           822:        if (evtimer_initialized(&cs->subs_timer))
        !           823:                evtimer_del(&cs->subs_timer);
        !           824:
1.35      nicm      825:        TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
                    826:                control_free_block(cs, cb);
                    827:        control_reset_offsets(c);
                    828:
1.33      nicm      829:        free(cs);
1.44    ! nicm      830: }
        !           831:
        !           832: /* Check session subscription. */
        !           833: static void
        !           834: control_check_subs_session(struct client *c, struct control_sub *csub)
        !           835: {
        !           836:        struct session          *s = c->session;
        !           837:        struct format_tree      *ft;
        !           838:        char                    *value;
        !           839:
        !           840:        ft = format_create_defaults(NULL, c, s, NULL, NULL);
        !           841:        value = format_expand(ft, csub->format);
        !           842:        format_free(ft);
        !           843:
        !           844:        if (csub->last != NULL && strcmp(value, csub->last) == 0) {
        !           845:                free(value);
        !           846:                return;
        !           847:        }
        !           848:        control_write(c,
        !           849:            "%%subscription-changed %s $%u - - - : %s",
        !           850:            csub->name, s->id, value);
        !           851:        free(csub->last);
        !           852:        csub->last = value;
        !           853: }
        !           854:
        !           855: /* Check pane subscription. */
        !           856: static void
        !           857: control_check_subs_pane(struct client *c, struct control_sub *csub)
        !           858: {
        !           859:        struct session          *s = c->session;
        !           860:        struct window_pane      *wp;
        !           861:        struct window           *w;
        !           862:        struct winlink          *wl;
        !           863:        struct format_tree      *ft;
        !           864:        char                    *value;
        !           865:        struct control_sub_pane *csp, find;
        !           866:
        !           867:        wp = window_pane_find_by_id(csub->id);
        !           868:        if (wp == NULL)
        !           869:                return;
        !           870:        w = wp->window;
        !           871:
        !           872:        TAILQ_FOREACH(wl, &w->winlinks, wentry) {
        !           873:                if (wl->session != s)
        !           874:                        continue;
        !           875:
        !           876:                ft = format_create_defaults(NULL, c, s, wl, wp);
        !           877:                value = format_expand(ft, csub->format);
        !           878:                format_free(ft);
        !           879:
        !           880:                find.pane = wp->id;
        !           881:                find.idx = wl->idx;
        !           882:
        !           883:                csp = RB_FIND(control_sub_panes, &csub->panes, &find);
        !           884:                if (csp == NULL) {
        !           885:                        csp = xcalloc(1, sizeof *csp);
        !           886:                        csp->pane = wp->id;
        !           887:                        csp->idx = wl->idx;
        !           888:                        RB_INSERT(control_sub_panes, &csub->panes, csp);
        !           889:                }
        !           890:
        !           891:                if (csp->last != NULL && strcmp(value, csp->last) == 0) {
        !           892:                        free(value);
        !           893:                        continue;
        !           894:                }
        !           895:                control_write(c,
        !           896:                    "%%subscription-changed %s $%u @%u %u %%%u : %s",
        !           897:                    csub->name, s->id, w->id, wl->idx, wp->id, value);
        !           898:                free(csp->last);
        !           899:                csp->last = value;
        !           900:        }
        !           901: }
        !           902:
        !           903: /* Check all panes subscription. */
        !           904: static void
        !           905: control_check_subs_all_panes(struct client *c, struct control_sub *csub)
        !           906: {
        !           907:        struct session          *s = c->session;
        !           908:        struct window_pane      *wp;
        !           909:        struct window           *w;
        !           910:        struct winlink          *wl;
        !           911:        struct format_tree      *ft;
        !           912:        char                    *value;
        !           913:        struct control_sub_pane *csp, find;
        !           914:
        !           915:        RB_FOREACH(wl, winlinks, &s->windows) {
        !           916:                w = wl->window;
        !           917:                TAILQ_FOREACH(wp, &w->panes, entry) {
        !           918:                        ft = format_create_defaults(NULL, c, s, wl, wp);
        !           919:                        value = format_expand(ft, csub->format);
        !           920:                        format_free(ft);
        !           921:
        !           922:                        find.pane = wp->id;
        !           923:                        find.idx = wl->idx;
        !           924:
        !           925:                        csp = RB_FIND(control_sub_panes, &csub->panes, &find);
        !           926:                        if (csp == NULL) {
        !           927:                                csp = xcalloc(1, sizeof *csp);
        !           928:                                csp->pane = wp->id;
        !           929:                                csp->idx = wl->idx;
        !           930:                                RB_INSERT(control_sub_panes, &csub->panes, csp);
        !           931:                        }
        !           932:
        !           933:                        if (csp->last != NULL &&
        !           934:                            strcmp(value, csp->last) == 0) {
        !           935:                                free(value);
        !           936:                                continue;
        !           937:                        }
        !           938:                        control_write(c,
        !           939:                            "%%subscription-changed %s $%u @%u %u %%%u : %s",
        !           940:                            csub->name, s->id, w->id, wl->idx, wp->id, value);
        !           941:                        free(csp->last);
        !           942:                        csp->last = value;
        !           943:                }
        !           944:        }
        !           945: }
        !           946:
        !           947: /* Check window subscription. */
        !           948: static void
        !           949: control_check_subs_window(struct client *c, struct control_sub *csub)
        !           950: {
        !           951:        struct session                  *s = c->session;
        !           952:        struct window                   *w;
        !           953:        struct winlink                  *wl;
        !           954:        struct format_tree              *ft;
        !           955:        char                            *value;
        !           956:        struct control_sub_window       *csw, find;
        !           957:
        !           958:        w = window_find_by_id(csub->id);
        !           959:        if (w == NULL)
        !           960:                return;
        !           961:
        !           962:        TAILQ_FOREACH(wl, &w->winlinks, wentry) {
        !           963:                if (wl->session != s)
        !           964:                        continue;
        !           965:
        !           966:                ft = format_create_defaults(NULL, c, s, wl, NULL);
        !           967:                value = format_expand(ft, csub->format);
        !           968:                format_free(ft);
        !           969:
        !           970:                find.window = w->id;
        !           971:                find.idx = wl->idx;
        !           972:
        !           973:                csw = RB_FIND(control_sub_windows, &csub->windows, &find);
        !           974:                if (csw == NULL) {
        !           975:                        csw = xcalloc(1, sizeof *csw);
        !           976:                        csw->window = w->id;
        !           977:                        csw->idx = wl->idx;
        !           978:                        RB_INSERT(control_sub_windows, &csub->windows, csw);
        !           979:                }
        !           980:
        !           981:                if (csw->last != NULL && strcmp(value, csw->last) == 0) {
        !           982:                        free(value);
        !           983:                        continue;
        !           984:                }
        !           985:                control_write(c,
        !           986:                    "%%subscription-changed %s $%u @%u %u - : %s",
        !           987:                    csub->name, s->id, w->id, wl->idx, value);
        !           988:                free(csw->last);
        !           989:                csw->last = value;
        !           990:        }
        !           991: }
        !           992:
        !           993: /* Check all windows subscription. */
        !           994: static void
        !           995: control_check_subs_all_windows(struct client *c, struct control_sub *csub)
        !           996: {
        !           997:        struct session                  *s = c->session;
        !           998:        struct window                   *w;
        !           999:        struct winlink                  *wl;
        !          1000:        struct format_tree              *ft;
        !          1001:        char                            *value;
        !          1002:        struct control_sub_window       *csw, find;
        !          1003:
        !          1004:        RB_FOREACH(wl, winlinks, &s->windows) {
        !          1005:                w = wl->window;
        !          1006:
        !          1007:                ft = format_create_defaults(NULL, c, s, wl, NULL);
        !          1008:                value = format_expand(ft, csub->format);
        !          1009:                format_free(ft);
        !          1010:
        !          1011:                find.window = w->id;
        !          1012:                find.idx = wl->idx;
        !          1013:
        !          1014:                csw = RB_FIND(control_sub_windows, &csub->windows, &find);
        !          1015:                if (csw == NULL) {
        !          1016:                        csw = xcalloc(1, sizeof *csw);
        !          1017:                        csw->window = w->id;
        !          1018:                        csw->idx = wl->idx;
        !          1019:                        RB_INSERT(control_sub_windows, &csub->windows, csw);
        !          1020:                }
        !          1021:
        !          1022:                if (csw->last != NULL && strcmp(value, csw->last) == 0) {
        !          1023:                        free(value);
        !          1024:                        continue;
        !          1025:                }
        !          1026:                control_write(c,
        !          1027:                    "%%subscription-changed %s $%u @%u %u - : %s",
        !          1028:                    csub->name, s->id, w->id, wl->idx, value);
        !          1029:                free(csw->last);
        !          1030:                csw->last = value;
        !          1031:        }
        !          1032: }
        !          1033:
        !          1034: /* Check subscriptions timer. */
        !          1035: static void
        !          1036: control_check_subs_timer(__unused int fd, __unused short events, void *data)
        !          1037: {
        !          1038:        struct client           *c = data;
        !          1039:        struct control_state    *cs = c->control_state;
        !          1040:        struct control_sub      *csub, *csub1;
        !          1041:        struct timeval           tv = { .tv_sec = 1 };
        !          1042:
        !          1043:        log_debug("%s: timer fired", __func__);
        !          1044:        evtimer_add(&cs->subs_timer, &tv);
        !          1045:
        !          1046:        RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
        !          1047:                switch (csub->type) {
        !          1048:                case CONTROL_SUB_SESSION:
        !          1049:                        control_check_subs_session(c, csub);
        !          1050:                        break;
        !          1051:                case CONTROL_SUB_PANE:
        !          1052:                        control_check_subs_pane(c, csub);
        !          1053:                        break;
        !          1054:                case CONTROL_SUB_ALL_PANES:
        !          1055:                        control_check_subs_all_panes(c, csub);
        !          1056:                        break;
        !          1057:                case CONTROL_SUB_WINDOW:
        !          1058:                        control_check_subs_window(c, csub);
        !          1059:                        break;
        !          1060:                case CONTROL_SUB_ALL_WINDOWS:
        !          1061:                        control_check_subs_all_windows(c, csub);
        !          1062:                        break;
        !          1063:                }
        !          1064:        }
        !          1065: }
        !          1066:
        !          1067: /* Add a subscription. */
        !          1068: void
        !          1069: control_add_sub(struct client *c, const char *name, enum control_sub_type type,
        !          1070:     int id, const char *format)
        !          1071: {
        !          1072:        struct control_state    *cs = c->control_state;
        !          1073:        struct control_sub      *csub, find;
        !          1074:        struct timeval           tv = { .tv_sec = 1 };
        !          1075:
        !          1076:        find.name = (char *)name;
        !          1077:        if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
        !          1078:                control_free_sub(cs, csub);
        !          1079:
        !          1080:        csub = xcalloc(1, sizeof *csub);
        !          1081:        csub->name = xstrdup(name);
        !          1082:        csub->type = type;
        !          1083:        csub->id = id;
        !          1084:        csub->format = xstrdup(format);
        !          1085:        RB_INSERT(control_subs, &cs->subs, csub);
        !          1086:
        !          1087:        RB_INIT(&csub->panes);
        !          1088:        RB_INIT(&csub->windows);
        !          1089:
        !          1090:        if (!evtimer_initialized(&cs->subs_timer))
        !          1091:                evtimer_set(&cs->subs_timer, control_check_subs_timer, c);
        !          1092:        if (!evtimer_pending(&cs->subs_timer, NULL))
        !          1093:                evtimer_add(&cs->subs_timer, &tv);
        !          1094: }
        !          1095:
        !          1096: /* Remove a subscription. */
        !          1097: void
        !          1098: control_remove_sub(struct client *c, const char *name)
        !          1099: {
        !          1100:        struct control_state    *cs = c->control_state;
        !          1101:        struct control_sub      *csub, find;
        !          1102:
        !          1103:        find.name = (char *)name;
        !          1104:        if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
        !          1105:                control_free_sub(cs, csub);
        !          1106:        if (RB_EMPTY(&cs->subs))
        !          1107:                evtimer_del(&cs->subs_timer);
1.1       nicm     1108: }