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

1.46    ! nicm        1: /* $OpenBSD: control.c,v 1.45 2020/09/18 11:20:59 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);
1.46    ! nicm      668:        if (wp == NULL || wp->fd == -1) {
1.36      nicm      669:                TAILQ_FOREACH_SAFE(cb, &cp->blocks, entry, cb1) {
                    670:                        TAILQ_REMOVE(&cp->blocks, cb, entry);
1.35      nicm      671:                        control_free_block(cs, cb);
1.36      nicm      672:                }
1.35      nicm      673:                control_flush_all_blocks(c);
                    674:                return (0);
                    675:        }
                    676:
                    677:        while (used != limit && !TAILQ_EMPTY(&cp->blocks)) {
1.42      nicm      678:                if (control_check_age(c, wp, cp)) {
                    679:                        if (message != NULL)
                    680:                                evbuffer_free(message);
                    681:                        message = NULL;
                    682:                        break;
                    683:                }
                    684:
1.35      nicm      685:                cb = TAILQ_FIRST(&cp->blocks);
1.39      nicm      686:                if (cb->t < t)
                    687:                        age = t - cb->t;
                    688:                else
                    689:                        age = 0;
                    690:                log_debug("%s: %s: output block %zu (age %llu) for %%%u "
1.45      nicm      691:                    "(used %zu/%zu)", __func__, c->name, cb->size,
                    692:                    (unsigned long long)age, cp->pane, used, limit);
1.35      nicm      693:
                    694:                size = cb->size;
                    695:                if (size > limit - used)
                    696:                        size = limit - used;
                    697:                used += size;
                    698:
1.39      nicm      699:                message = control_append_data(c, cp, age, message, wp, size);
1.35      nicm      700:
                    701:                cb->size -= size;
                    702:                if (cb->size == 0) {
                    703:                        TAILQ_REMOVE(&cp->blocks, cb, entry);
                    704:                        control_free_block(cs, cb);
                    705:
                    706:                        cb = TAILQ_FIRST(&cs->all_blocks);
                    707:                        if (cb != NULL && cb->size == 0) {
                    708:                                if (wp != NULL && message != NULL) {
                    709:                                        control_write_data(c, message);
                    710:                                        message = NULL;
                    711:                                }
                    712:                                control_flush_all_blocks(c);
                    713:                        }
                    714:                }
                    715:        }
                    716:        if (message != NULL)
                    717:                control_write_data(c, message);
                    718:        return (!TAILQ_EMPTY(&cp->blocks));
                    719: }
                    720:
                    721: /* Control client write callback. */
                    722: static void
                    723: control_write_callback(__unused struct bufferevent *bufev, void *data)
                    724: {
                    725:        struct client           *c = data;
                    726:        struct control_state    *cs = c->control_state;
                    727:        struct control_pane     *cp, *cp1;
                    728:        struct evbuffer         *evb = cs->write_event->output;
                    729:        size_t                   space, limit;
                    730:
                    731:        control_flush_all_blocks(c);
                    732:
                    733:        while (EVBUFFER_LENGTH(evb) < CONTROL_BUFFER_HIGH) {
                    734:                if (cs->pending_count == 0)
                    735:                        break;
                    736:                space = CONTROL_BUFFER_HIGH - EVBUFFER_LENGTH(evb);
                    737:                log_debug("%s: %s: %zu bytes available, %u panes", __func__,
                    738:                    c->name, space, cs->pending_count);
                    739:
                    740:                limit = (space / cs->pending_count / 3); /* 3 bytes for \xxx */
                    741:                if (limit < CONTROL_WRITE_MINIMUM)
                    742:                        limit = CONTROL_WRITE_MINIMUM;
                    743:
                    744:                TAILQ_FOREACH_SAFE(cp, &cs->pending_list, pending_entry, cp1) {
                    745:                        if (EVBUFFER_LENGTH(evb) >= CONTROL_BUFFER_HIGH)
                    746:                                break;
                    747:                        if (control_write_pending(c, cp, limit))
                    748:                                continue;
                    749:                        TAILQ_REMOVE(&cs->pending_list, cp, pending_entry);
                    750:                        cp->pending_flag = 0;
                    751:                        cs->pending_count--;
                    752:                }
                    753:        }
                    754:        if (EVBUFFER_LENGTH(evb) == 0)
                    755:                bufferevent_disable(cs->write_event, EV_WRITE);
                    756: }
                    757:
1.32      nicm      758: /* Initialize for control mode. */
1.25      nicm      759: void
                    760: control_start(struct client *c)
                    761: {
1.33      nicm      762:        struct control_state    *cs;
                    763:
1.34      nicm      764:        if (c->flags & CLIENT_CONTROLCONTROL) {
                    765:                close(c->out_fd);
                    766:                c->out_fd = -1;
                    767:        } else
                    768:                setblocking(c->out_fd, 0);
                    769:        setblocking(c->fd, 0);
                    770:
1.33      nicm      771:        cs = c->control_state = xcalloc(1, sizeof *cs);
1.35      nicm      772:        RB_INIT(&cs->panes);
                    773:        TAILQ_INIT(&cs->pending_list);
                    774:        TAILQ_INIT(&cs->all_blocks);
1.44      nicm      775:        RB_INIT(&cs->subs);
1.33      nicm      776:
1.35      nicm      777:        cs->read_event = bufferevent_new(c->fd, control_read_callback,
                    778:            control_write_callback, control_error_callback, c);
1.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);
1.46    ! nicm      868:        if (wp == NULL || wp->fd == -1)
1.44      nicm      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: }