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

Annotation of src/usr.bin/tmux/cmd-queue.c, Revision 1.112

1.112   ! nicm        1: /* $OpenBSD: cmd-queue.c,v 1.111 2022/12/07 09:44:44 nicm Exp $ */
1.1       nicm        2:
                      3: /*
1.34      nicm        4:  * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1       nicm        5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
                     15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
                     16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18:
                     19: #include <sys/types.h>
                     20:
                     21: #include <ctype.h>
1.108     nicm       22: #include <pwd.h>
1.1       nicm       23: #include <stdlib.h>
1.23      nicm       24: #include <string.h>
1.7       nicm       25: #include <time.h>
1.108     nicm       26: #include <unistd.h>
1.111     nicm       27: #include <vis.h>
1.1       nicm       28:
                     29: #include "tmux.h"
                     30:
1.84      nicm       31: /* Command queue flags. */
                     32: #define CMDQ_FIRED 0x1
                     33: #define CMDQ_WAITING 0x2
                     34:
1.83      nicm       35: /* Command queue item type. */
                     36: enum cmdq_type {
                     37:        CMDQ_COMMAND,
                     38:        CMDQ_CALLBACK,
                     39: };
                     40:
                     41: /* Command queue item. */
                     42: struct cmdq_item {
                     43:        char                    *name;
                     44:        struct cmdq_list        *queue;
                     45:        struct cmdq_item        *next;
                     46:
                     47:        struct client           *client;
1.89      nicm       48:        struct client           *target_client;
1.83      nicm       49:
                     50:        enum cmdq_type           type;
                     51:        u_int                    group;
                     52:
                     53:        u_int                    number;
                     54:        time_t                   time;
                     55:
                     56:        int                      flags;
                     57:
1.86      nicm       58:        struct cmdq_state       *state;
1.83      nicm       59:        struct cmd_find_state    source;
                     60:        struct cmd_find_state    target;
                     61:
                     62:        struct cmd_list         *cmdlist;
                     63:        struct cmd              *cmd;
                     64:
                     65:        cmdq_cb                  cb;
                     66:        void                    *data;
                     67:
                     68:        TAILQ_ENTRY(cmdq_item)   entry;
                     69: };
1.90      nicm       70: TAILQ_HEAD(cmdq_item_list, cmdq_item);
1.24      nicm       71:
1.87      nicm       72: /*
                     73:  * Command queue state. This is the context for commands on the command queue.
                     74:  * It holds information about how the commands were fired (the key and flags),
                     75:  * any additional formats for the commands, and the current default target.
                     76:  * Multiple commands can share the same state and a command may update the
                     77:  * default target.
                     78:  */
                     79: struct cmdq_state {
                     80:        int                      references;
                     81:        int                      flags;
                     82:
                     83:        struct format_tree      *formats;
                     84:
                     85:        struct key_event         event;
                     86:        struct cmd_find_state    current;
                     87: };
                     88:
1.90      nicm       89: /* Command queue. */
                     90: struct cmdq_list {
                     91:        struct cmdq_item        *item;
                     92:        struct cmdq_item_list    list;
                     93: };
                     94:
1.43      nicm       95: /* Get command queue name. */
                     96: static const char *
                     97: cmdq_name(struct client *c)
                     98: {
1.70      nicm       99:        static char     s[256];
1.43      nicm      100:
                    101:        if (c == NULL)
                    102:                return ("<global>");
1.71      nicm      103:        if (c->name != NULL)
                    104:                xsnprintf(s, sizeof s, "<%s>", c->name);
                    105:        else
                    106:                xsnprintf(s, sizeof s, "<%p>", c);
1.43      nicm      107:        return (s);
                    108: }
                    109:
                    110: /* Get command queue from client. */
1.44      nicm      111: static struct cmdq_list *
1.43      nicm      112: cmdq_get(struct client *c)
                    113: {
1.83      nicm      114:        static struct cmdq_list *global_queue;
                    115:
                    116:        if (c == NULL) {
                    117:                if (global_queue == NULL)
                    118:                        global_queue = cmdq_new();
                    119:                return (global_queue);
                    120:        }
                    121:        return (c->queue);
                    122: }
                    123:
                    124: /* Create a queue. */
                    125: struct cmdq_list *
                    126: cmdq_new(void)
                    127: {
                    128:        struct cmdq_list        *queue;
                    129:
1.109     nicm      130:        queue = xcalloc(1, sizeof *queue);
1.90      nicm      131:        TAILQ_INIT (&queue->list);
1.83      nicm      132:        return (queue);
                    133: }
                    134:
                    135: /* Free a queue. */
                    136: void
                    137: cmdq_free(struct cmdq_list *queue)
                    138: {
1.90      nicm      139:        if (!TAILQ_EMPTY(&queue->list))
1.83      nicm      140:                fatalx("queue not empty");
                    141:        free(queue);
                    142: }
                    143:
                    144: /* Get item name. */
                    145: const char *
                    146: cmdq_get_name(struct cmdq_item *item)
                    147: {
                    148:        return (item->name);
                    149: }
                    150:
                    151: /* Get item client. */
                    152: struct client *
                    153: cmdq_get_client(struct cmdq_item *item)
                    154: {
                    155:        return (item->client);
                    156: }
                    157:
1.89      nicm      158: /* Get item target client. */
                    159: struct client *
                    160: cmdq_get_target_client(struct cmdq_item *item)
                    161: {
                    162:        return (item->target_client);
                    163: }
                    164:
1.88      nicm      165: /* Get item state. */
                    166: struct cmdq_state *
                    167: cmdq_get_state(struct cmdq_item *item)
                    168: {
                    169:        return (item->state);
                    170: }
                    171:
1.83      nicm      172: /* Get item target. */
                    173: struct cmd_find_state *
                    174: cmdq_get_target(struct cmdq_item *item)
                    175: {
                    176:        return (&item->target);
                    177: }
                    178:
                    179: /* Get item source. */
                    180: struct cmd_find_state *
                    181: cmdq_get_source(struct cmdq_item *item)
                    182: {
                    183:        return (&item->source);
                    184: }
                    185:
1.87      nicm      186: /* Get state event. */
                    187: struct key_event *
                    188: cmdq_get_event(struct cmdq_item *item)
                    189: {
                    190:        return (&item->state->event);
                    191: }
                    192:
                    193: /* Get state current target. */
                    194: struct cmd_find_state *
                    195: cmdq_get_current(struct cmdq_item *item)
                    196: {
                    197:        return (&item->state->current);
                    198: }
                    199:
                    200: /* Get state flags. */
                    201: int
                    202: cmdq_get_flags(struct cmdq_item *item)
1.83      nicm      203: {
1.87      nicm      204:        return (item->state->flags);
1.83      nicm      205: }
                    206:
1.88      nicm      207: /* Create a new state. */
                    208: struct cmdq_state *
                    209: cmdq_new_state(struct cmd_find_state *current, struct key_event *event,
                    210:     int flags)
                    211: {
                    212:        struct cmdq_state       *state;
                    213:
                    214:        state = xcalloc(1, sizeof *state);
                    215:        state->references = 1;
                    216:        state->flags = flags;
                    217:
                    218:        if (event != NULL)
                    219:                memcpy(&state->event, event, sizeof state->event);
                    220:        else
                    221:                state->event.key = KEYC_NONE;
                    222:        if (current != NULL && cmd_find_valid_state(current))
                    223:                cmd_find_copy_state(&state->current, current);
                    224:        else
                    225:                cmd_find_clear_state(&state->current, 0);
                    226:
                    227:        return (state);
                    228: }
                    229:
                    230: /* Add a reference to a state. */
                    231: struct cmdq_state *
                    232: cmdq_link_state(struct cmdq_state *state)
                    233: {
                    234:        state->references++;
                    235:        return (state);
                    236: }
                    237:
                    238: /* Make a copy of a state. */
                    239: struct cmdq_state *
                    240: cmdq_copy_state(struct cmdq_state *state)
                    241: {
                    242:        return (cmdq_new_state(&state->current, &state->event, state->flags));
                    243: }
                    244:
                    245: /* Free a state. */
                    246: void
                    247: cmdq_free_state(struct cmdq_state *state)
                    248: {
1.91      nicm      249:        if (--state->references != 0)
                    250:                return;
                    251:
                    252:        if (state->formats != NULL)
                    253:                format_free(state->formats);
                    254:        free(state);
1.88      nicm      255: }
                    256:
                    257: /* Add a format to command queue. */
                    258: void
                    259: cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...)
                    260: {
                    261:        va_list  ap;
                    262:        char    *value;
                    263:
                    264:        va_start(ap, fmt);
                    265:        xvasprintf(&value, fmt, ap);
                    266:        va_end(ap);
                    267:
                    268:        if (state->formats == NULL)
                    269:                state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
                    270:        format_add(state->formats, key, "%s", value);
                    271:
                    272:        free(value);
1.103     nicm      273: }
                    274:
                    275: /* Add formats to command queue. */
                    276: void
                    277: cmdq_add_formats(struct cmdq_state *state, struct format_tree *ft)
                    278: {
                    279:        if (state->formats == NULL)
                    280:                state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
                    281:        format_merge(state->formats, ft);
1.88      nicm      282: }
                    283:
1.83      nicm      284: /* Merge formats from item. */
                    285: void
                    286: cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
                    287: {
                    288:        const struct cmd_entry  *entry;
                    289:
                    290:        if (item->cmd != NULL) {
1.101     nicm      291:                entry = cmd_get_entry(item->cmd);
1.83      nicm      292:                format_add(ft, "command", "%s", entry->name);
                    293:        }
1.86      nicm      294:        if (item->state->formats != NULL)
                    295:                format_merge(ft, item->state->formats);
1.43      nicm      296: }
                    297:
                    298: /* Append an item. */
1.78      nicm      299: struct cmdq_item *
1.44      nicm      300: cmdq_append(struct client *c, struct cmdq_item *item)
1.43      nicm      301: {
1.44      nicm      302:        struct cmdq_list        *queue = cmdq_get(c);
                    303:        struct cmdq_item        *next;
1.43      nicm      304:
                    305:        do {
1.44      nicm      306:                next = item->next;
                    307:                item->next = NULL;
1.43      nicm      308:
                    309:                if (c != NULL)
                    310:                        c->references++;
1.44      nicm      311:                item->client = c;
1.43      nicm      312:
1.44      nicm      313:                item->queue = queue;
1.90      nicm      314:                TAILQ_INSERT_TAIL(&queue->list, item, entry);
1.64      nicm      315:                log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
1.43      nicm      316:
1.44      nicm      317:                item = next;
                    318:        } while (item != NULL);
1.90      nicm      319:        return (TAILQ_LAST(&queue->list, cmdq_item_list));
1.43      nicm      320: }
                    321:
                    322: /* Insert an item. */
1.78      nicm      323: struct cmdq_item *
1.44      nicm      324: cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
1.43      nicm      325: {
                    326:        struct client           *c = after->client;
1.44      nicm      327:        struct cmdq_list        *queue = after->queue;
                    328:        struct cmdq_item        *next;
1.43      nicm      329:
                    330:        do {
1.44      nicm      331:                next = item->next;
1.64      nicm      332:                item->next = after->next;
                    333:                after->next = item;
1.43      nicm      334:
                    335:                if (c != NULL)
                    336:                        c->references++;
1.44      nicm      337:                item->client = c;
1.43      nicm      338:
1.44      nicm      339:                item->queue = queue;
1.90      nicm      340:                TAILQ_INSERT_AFTER(&queue->list, after, item, entry);
1.64      nicm      341:                log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
                    342:                    item->name, after->name);
1.43      nicm      343:
1.65      nicm      344:                after = item;
1.44      nicm      345:                item = next;
                    346:        } while (item != NULL);
1.78      nicm      347:        return (after);
1.43      nicm      348: }
1.63      nicm      349:
                    350: /* Insert a hook. */
                    351: void
                    352: cmdq_insert_hook(struct session *s, struct cmdq_item *item,
1.88      nicm      353:     struct cmd_find_state *current, const char *fmt, ...)
1.63      nicm      354: {
1.88      nicm      355:        struct cmdq_state               *state = item->state;
1.92      nicm      356:        struct cmd                      *cmd = item->cmd;
                    357:        struct args                     *args = cmd_get_args(cmd);
1.104     nicm      358:        struct args_entry               *ae;
                    359:        struct args_value               *av;
1.63      nicm      360:        struct options                  *oo;
                    361:        va_list                          ap;
1.92      nicm      362:        char                            *name, tmp[32], flag, *arguments;
1.105     nicm      363:        u_int                            i;
1.92      nicm      364:        const char                      *value;
1.63      nicm      365:        struct cmdq_item                *new_item;
1.88      nicm      366:        struct cmdq_state               *new_state;
1.63      nicm      367:        struct options_entry            *o;
                    368:        struct options_array_item       *a;
                    369:        struct cmd_list                 *cmdlist;
                    370:
1.86      nicm      371:        if (item->state->flags & CMDQ_STATE_NOHOOKS)
1.63      nicm      372:                return;
                    373:        if (s == NULL)
                    374:                oo = global_s_options;
                    375:        else
                    376:                oo = s->options;
                    377:
                    378:        va_start(ap, fmt);
                    379:        xvasprintf(&name, fmt, ap);
                    380:        va_end(ap);
                    381:
                    382:        o = options_get(oo, name);
                    383:        if (o == NULL) {
                    384:                free(name);
                    385:                return;
                    386:        }
                    387:        log_debug("running hook %s (parent %p)", name, item);
                    388:
1.88      nicm      389:        /*
                    390:         * The hooks get a new state because they should not update the current
                    391:         * target or formats for any subsequent commands.
                    392:         */
                    393:        new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
                    394:        cmdq_add_format(new_state, "hook", "%s", name);
1.92      nicm      395:
                    396:        arguments = args_print(args);
                    397:        cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
                    398:        free(arguments);
                    399:
1.105     nicm      400:        for (i = 0; i < args_count(args); i++) {
1.92      nicm      401:                xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
1.105     nicm      402:                cmdq_add_format(new_state, tmp, "%s", args_string(args, i));
1.92      nicm      403:        }
1.104     nicm      404:        flag = args_first(args, &ae);
1.92      nicm      405:        while (flag != 0) {
                    406:                value = args_get(args, flag);
                    407:                if (value == NULL) {
                    408:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
                    409:                        cmdq_add_format(new_state, tmp, "1");
                    410:                } else {
                    411:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
                    412:                        cmdq_add_format(new_state, tmp, "%s", value);
                    413:                }
                    414:
                    415:                i = 0;
1.104     nicm      416:                av = args_first_value(args, flag);
                    417:                while (av != NULL) {
1.92      nicm      418:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
1.106     nicm      419:                        cmdq_add_format(new_state, tmp, "%s", av->string);
1.92      nicm      420:                        i++;
1.104     nicm      421:                        av = args_next_value(av);
1.92      nicm      422:                }
                    423:
1.104     nicm      424:                flag = args_next(&ae);
1.92      nicm      425:        }
1.88      nicm      426:
1.63      nicm      427:        a = options_array_first(o);
                    428:        while (a != NULL) {
                    429:                cmdlist = options_array_item_value(a)->cmdlist;
1.88      nicm      430:                if (cmdlist != NULL) {
                    431:                        new_item = cmdq_get_command(cmdlist, new_state);
                    432:                        if (item != NULL)
                    433:                                item = cmdq_insert_after(item, new_item);
                    434:                        else
                    435:                                item = cmdq_append(NULL, new_item);
1.63      nicm      436:                }
                    437:                a = options_array_next(a);
                    438:        }
                    439:
1.88      nicm      440:        cmdq_free_state(new_state);
1.63      nicm      441:        free(name);
1.74      nicm      442: }
                    443:
                    444: /* Continue processing command queue. */
                    445: void
                    446: cmdq_continue(struct cmdq_item *item)
                    447: {
                    448:        item->flags &= ~CMDQ_WAITING;
1.63      nicm      449: }
                    450:
1.43      nicm      451: /* Remove an item. */
                    452: static void
1.44      nicm      453: cmdq_remove(struct cmdq_item *item)
1.43      nicm      454: {
1.44      nicm      455:        if (item->client != NULL)
                    456:                server_client_unref(item->client);
1.62      nicm      457:        if (item->cmdlist != NULL)
1.44      nicm      458:                cmd_list_free(item->cmdlist);
1.88      nicm      459:        cmdq_free_state(item->state);
1.43      nicm      460:
1.90      nicm      461:        TAILQ_REMOVE(&item->queue->list, item, entry);
1.46      nicm      462:
1.64      nicm      463:        free(item->name);
1.44      nicm      464:        free(item);
1.43      nicm      465: }
                    466:
                    467: /* Remove all subsequent items that match this item's group. */
                    468: static void
1.44      nicm      469: cmdq_remove_group(struct cmdq_item *item)
1.43      nicm      470: {
1.44      nicm      471:        struct cmdq_item        *this, *next;
1.43      nicm      472:
1.69      nicm      473:        if (item->group == 0)
                    474:                return;
1.44      nicm      475:        this = TAILQ_NEXT(item, entry);
1.43      nicm      476:        while (this != NULL) {
                    477:                next = TAILQ_NEXT(this, entry);
1.44      nicm      478:                if (this->group == item->group)
1.43      nicm      479:                        cmdq_remove(this);
                    480:                this = next;
                    481:        }
                    482: }
                    483:
1.107     nicm      484: /* Empty command callback. */
                    485: static enum cmd_retval
                    486: cmdq_empty_command(__unused struct cmdq_item *item, __unused void *data)
                    487: {
                    488:        return (CMD_RETURN_NORMAL);
                    489: }
                    490:
1.43      nicm      491: /* Get a command for the command queue. */
1.44      nicm      492: struct cmdq_item *
1.88      nicm      493: cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
1.43      nicm      494: {
1.44      nicm      495:        struct cmdq_item        *item, *first = NULL, *last = NULL;
                    496:        struct cmd              *cmd;
1.82      nicm      497:        const struct cmd_entry  *entry;
1.88      nicm      498:        int                      created = 0;
1.49      nicm      499:
1.107     nicm      500:        if ((cmd = cmd_list_first(cmdlist)) == NULL)
                    501:                return (cmdq_get_callback(cmdq_empty_command, NULL));
                    502:
1.88      nicm      503:        if (state == NULL) {
                    504:                state = cmdq_new_state(NULL, NULL, 0);
                    505:                created = 1;
                    506:        }
                    507:
1.82      nicm      508:        while (cmd != NULL) {
                    509:                entry = cmd_get_entry(cmd);
1.43      nicm      510:
1.44      nicm      511:                item = xcalloc(1, sizeof *item);
1.82      nicm      512:                xasprintf(&item->name, "[%s/%p]", entry->name, item);
1.44      nicm      513:                item->type = CMDQ_COMMAND;
1.43      nicm      514:
1.88      nicm      515:                item->group = cmd_get_group(cmd);
                    516:                item->state = cmdq_link_state(state);
                    517:
1.44      nicm      518:                item->cmdlist = cmdlist;
                    519:                item->cmd = cmd;
1.68      nicm      520:
1.88      nicm      521:                cmdlist->references++;
1.68      nicm      522:                log_debug("%s: %s group %u", __func__, item->name, item->group);
1.43      nicm      523:
                    524:                if (first == NULL)
1.44      nicm      525:                        first = item;
1.43      nicm      526:                if (last != NULL)
1.44      nicm      527:                        last->next = item;
                    528:                last = item;
1.82      nicm      529:
1.88      nicm      530:                cmd = cmd_list_next(cmd);
1.43      nicm      531:        }
1.88      nicm      532:
                    533:        if (created)
                    534:                cmdq_free_state(state);
1.43      nicm      535:        return (first);
                    536: }
                    537:
1.54      nicm      538: /* Fill in flag for a command. */
                    539: static enum cmd_retval
                    540: cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
                    541:     const struct cmd_entry_flag *flag)
                    542: {
                    543:        const char      *value;
                    544:
                    545:        if (flag->flag == 0) {
1.95      nicm      546:                cmd_find_from_client(fs, item->target_client, 0);
1.54      nicm      547:                return (CMD_RETURN_NORMAL);
                    548:        }
                    549:
1.82      nicm      550:        value = args_get(cmd_get_args(item->cmd), flag->flag);
1.54      nicm      551:        if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
                    552:                cmd_find_clear_state(fs, 0);
                    553:                return (CMD_RETURN_ERROR);
                    554:        }
                    555:        return (CMD_RETURN_NORMAL);
                    556: }
                    557:
1.93      nicm      558: /* Add message with command. */
                    559: static void
                    560: cmdq_add_message(struct cmdq_item *item)
                    561: {
                    562:        struct client           *c = item->client;
                    563:        struct cmdq_state       *state = item->state;
1.108     nicm      564:        const char              *key;
1.93      nicm      565:        char                    *tmp;
1.108     nicm      566:        uid_t                    uid;
                    567:        struct passwd           *pw;
                    568:        char                    *user = NULL;
1.93      nicm      569:
                    570:        tmp = cmd_print(item->cmd);
                    571:        if (c != NULL) {
1.108     nicm      572:                uid = proc_get_peer_uid(c->peer);
                    573:                if (uid != (uid_t)-1 && uid != getuid()) {
                    574:                        if ((pw = getpwuid(uid)) != NULL)
                    575:                                xasprintf(&user, "[%s]", pw->pw_name);
                    576:                        else
                    577:                                user = xstrdup("[unknown]");
                    578:                } else
                    579:                        user = xstrdup("");
1.93      nicm      580:                if (c->session != NULL && state->event.key != KEYC_NONE) {
1.97      nicm      581:                        key = key_string_lookup_key(state->event.key, 0);
1.108     nicm      582:                        server_add_message("%s%s key %s: %s", c->name, user,
                    583:                            key, tmp);
                    584:                } else {
                    585:                        server_add_message("%s%s command: %s", c->name, user,
                    586:                            tmp);
                    587:                }
                    588:                free(user);
1.93      nicm      589:        } else
                    590:                server_add_message("command: %s", tmp);
                    591:        free(tmp);
                    592: }
                    593:
1.43      nicm      594: /* Fire command on command queue. */
                    595: static enum cmd_retval
1.44      nicm      596: cmdq_fire_command(struct cmdq_item *item)
1.43      nicm      597: {
1.89      nicm      598:        const char              *name = cmdq_name(item->client);
1.86      nicm      599:        struct cmdq_state       *state = item->state;
1.44      nicm      600:        struct cmd              *cmd = item->cmd;
1.89      nicm      601:        struct args             *args = cmd_get_args(cmd);
1.82      nicm      602:        const struct cmd_entry  *entry = cmd_get_entry(cmd);
1.89      nicm      603:        struct client           *tc, *saved = item->client;
1.43      nicm      604:        enum cmd_retval          retval;
                    605:        struct cmd_find_state   *fsp, fs;
1.89      nicm      606:        int                      flags, quiet = 0;
1.72      nicm      607:        char                    *tmp;
                    608:
1.93      nicm      609:        if (cfg_finished)
                    610:                cmdq_add_message(item);
1.72      nicm      611:        if (log_get_level() > 1) {
                    612:                tmp = cmd_print(cmd);
                    613:                log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
                    614:                free(tmp);
                    615:        }
1.43      nicm      616:
1.86      nicm      617:        flags = !!(state->flags & CMDQ_STATE_CONTROL);
1.44      nicm      618:        cmdq_guard(item, "begin", flags);
1.43      nicm      619:
1.53      nicm      620:        if (item->client == NULL)
                    621:                item->client = cmd_find_client(item, NULL, 1);
1.89      nicm      622:
                    623:        if (entry->flags & CMD_CLIENT_CANFAIL)
                    624:                quiet = 1;
                    625:        if (entry->flags & CMD_CLIENT_CFLAG) {
                    626:                tc = cmd_find_client(item, args_get(args, 'c'), quiet);
                    627:                if (tc == NULL && !quiet) {
                    628:                        retval = CMD_RETURN_ERROR;
                    629:                        goto out;
                    630:                }
                    631:        } else if (entry->flags & CMD_CLIENT_TFLAG) {
                    632:                tc = cmd_find_client(item, args_get(args, 't'), quiet);
                    633:                if (tc == NULL && !quiet) {
                    634:                        retval = CMD_RETURN_ERROR;
                    635:                        goto out;
                    636:                }
                    637:        } else
                    638:                tc = cmd_find_client(item, NULL, 1);
                    639:        item->target_client = tc;
                    640:
1.54      nicm      641:        retval = cmdq_find_flag(item, &item->source, &entry->source);
                    642:        if (retval == CMD_RETURN_ERROR)
                    643:                goto out;
                    644:        retval = cmdq_find_flag(item, &item->target, &entry->target);
                    645:        if (retval == CMD_RETURN_ERROR)
1.43      nicm      646:                goto out;
1.89      nicm      647:
1.54      nicm      648:        retval = entry->exec(cmd, item);
1.43      nicm      649:        if (retval == CMD_RETURN_ERROR)
                    650:                goto out;
                    651:
1.54      nicm      652:        if (entry->flags & CMD_AFTERHOOK) {
                    653:                if (cmd_find_valid_state(&item->target))
                    654:                        fsp = &item->target;
1.86      nicm      655:                else if (cmd_find_valid_state(&item->state->current))
                    656:                        fsp = &item->state->current;
1.58      nicm      657:                else if (cmd_find_from_client(&fs, item->client, 0) == 0)
1.43      nicm      658:                        fsp = &fs;
1.51      nicm      659:                else
                    660:                        goto out;
1.63      nicm      661:                cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
1.43      nicm      662:        }
                    663:
                    664: out:
1.89      nicm      665:        item->client = saved;
1.43      nicm      666:        if (retval == CMD_RETURN_ERROR)
1.44      nicm      667:                cmdq_guard(item, "error", flags);
1.43      nicm      668:        else
1.44      nicm      669:                cmdq_guard(item, "end", flags);
1.43      nicm      670:        return (retval);
                    671: }
                    672:
                    673: /* Get a callback for the command queue. */
1.44      nicm      674: struct cmdq_item *
1.46      nicm      675: cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
1.1       nicm      676: {
1.44      nicm      677:        struct cmdq_item        *item;
1.1       nicm      678:
1.44      nicm      679:        item = xcalloc(1, sizeof *item);
1.64      nicm      680:        xasprintf(&item->name, "[%s/%p]", name, item);
1.44      nicm      681:        item->type = CMDQ_CALLBACK;
1.88      nicm      682:
1.44      nicm      683:        item->group = 0;
1.88      nicm      684:        item->state = cmdq_new_state(NULL, NULL, 0);
1.1       nicm      685:
1.44      nicm      686:        item->cb = cb;
                    687:        item->data = data;
1.1       nicm      688:
1.44      nicm      689:        return (item);
1.67      nicm      690: }
                    691:
                    692: /* Generic error callback. */
                    693: static enum cmd_retval
                    694: cmdq_error_callback(struct cmdq_item *item, void *data)
                    695: {
                    696:        char    *error = data;
                    697:
                    698:        cmdq_error(item, "%s", error);
                    699:        free(error);
                    700:
                    701:        return (CMD_RETURN_NORMAL);
                    702: }
                    703:
                    704: /* Get an error callback for the command queue. */
                    705: struct cmdq_item *
                    706: cmdq_get_error(const char *error)
                    707: {
                    708:        return (cmdq_get_callback(cmdq_error_callback, xstrdup(error)));
1.43      nicm      709: }
1.1       nicm      710:
1.43      nicm      711: /* Fire callback on callback queue. */
                    712: static enum cmd_retval
1.44      nicm      713: cmdq_fire_callback(struct cmdq_item *item)
1.43      nicm      714: {
1.44      nicm      715:        return (item->cb(item, item->data));
1.45      nicm      716: }
1.33      nicm      717:
1.43      nicm      718: /* Process next item on command queue. */
                    719: u_int
                    720: cmdq_next(struct client *c)
                    721: {
1.44      nicm      722:        struct cmdq_list        *queue = cmdq_get(c);
1.43      nicm      723:        const char              *name = cmdq_name(c);
1.44      nicm      724:        struct cmdq_item        *item;
1.43      nicm      725:        enum cmd_retval          retval;
                    726:        u_int                    items = 0;
                    727:        static u_int             number;
1.1       nicm      728:
1.90      nicm      729:        if (TAILQ_EMPTY(&queue->list)) {
1.43      nicm      730:                log_debug("%s %s: empty", __func__, name);
1.25      nicm      731:                return (0);
                    732:        }
1.90      nicm      733:        if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) {
1.43      nicm      734:                log_debug("%s %s: waiting", __func__, name);
                    735:                return (0);
                    736:        }
                    737:
                    738:        log_debug("%s %s: enter", __func__, name);
                    739:        for (;;) {
1.90      nicm      740:                item = queue->item = TAILQ_FIRST(&queue->list);
1.44      nicm      741:                if (item == NULL)
1.43      nicm      742:                        break;
1.46      nicm      743:                log_debug("%s %s: %s (%d), flags %x", __func__, name,
                    744:                    item->name, item->type, item->flags);
1.43      nicm      745:
                    746:                /*
                    747:                 * Any item with the waiting flag set waits until an external
                    748:                 * event clears the flag (for example, a job - look at
                    749:                 * run-shell).
                    750:                 */
1.44      nicm      751:                if (item->flags & CMDQ_WAITING)
1.43      nicm      752:                        goto waiting;
                    753:
                    754:                /*
                    755:                 * Items are only fired once, once the fired flag is set, a
                    756:                 * waiting flag can only be cleared by an external event.
                    757:                 */
1.44      nicm      758:                if (~item->flags & CMDQ_FIRED) {
                    759:                        item->time = time(NULL);
                    760:                        item->number = ++number;
1.43      nicm      761:
1.50      nicm      762:                        switch (item->type) {
1.44      nicm      763:                        case CMDQ_COMMAND:
                    764:                                retval = cmdq_fire_command(item);
1.43      nicm      765:
                    766:                                /*
                    767:                                 * If a command returns an error, remove any
                    768:                                 * subsequent commands in the same group.
                    769:                                 */
                    770:                                if (retval == CMD_RETURN_ERROR)
1.44      nicm      771:                                        cmdq_remove_group(item);
1.43      nicm      772:                                break;
1.44      nicm      773:                        case CMDQ_CALLBACK:
                    774:                                retval = cmdq_fire_callback(item);
1.43      nicm      775:                                break;
                    776:                        default:
                    777:                                retval = CMD_RETURN_ERROR;
                    778:                                break;
                    779:                        }
1.44      nicm      780:                        item->flags |= CMDQ_FIRED;
1.43      nicm      781:
                    782:                        if (retval == CMD_RETURN_WAIT) {
1.44      nicm      783:                                item->flags |= CMDQ_WAITING;
1.43      nicm      784:                                goto waiting;
                    785:                        }
                    786:                        items++;
                    787:                }
1.44      nicm      788:                cmdq_remove(item);
1.43      nicm      789:        }
1.90      nicm      790:        queue->item = NULL;
1.43      nicm      791:
                    792:        log_debug("%s %s: exit (empty)", __func__, name);
                    793:        return (items);
1.1       nicm      794:
1.43      nicm      795: waiting:
                    796:        log_debug("%s %s: exit (wait)", __func__, name);
                    797:        return (items);
1.90      nicm      798: }
                    799:
                    800: /* Get running item if any. */
                    801: struct cmdq_item *
                    802: cmdq_running(struct client *c)
                    803: {
                    804:        struct cmdq_list        *queue = cmdq_get(c);
                    805:
1.100     nicm      806:        if (queue->item == NULL)
                    807:         return (NULL);
                    808:     if (queue->item->flags & CMDQ_WAITING)
                    809:         return (NULL);
                    810:     return (queue->item);
1.43      nicm      811: }
                    812:
                    813: /* Print a guard line. */
                    814: void
1.44      nicm      815: cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
1.43      nicm      816: {
1.44      nicm      817:        struct client   *c = item->client;
1.76      nicm      818:        long             t = item->time;
                    819:        u_int            number = item->number;
1.43      nicm      820:
1.76      nicm      821:        if (c != NULL && (c->flags & CLIENT_CONTROL))
1.98      nicm      822:                control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
1.1       nicm      823: }
                    824:
                    825: /* Show message from command. */
1.18      nicm      826: void
1.111     nicm      827: cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb)
1.1       nicm      828: {
1.61      nicm      829:        struct client                   *c = item->client;
1.111     nicm      830:        void                            *data = EVBUFFER_DATA(evb);
                    831:        size_t                           size = EVBUFFER_LENGTH(evb);
1.61      nicm      832:        struct window_pane              *wp;
                    833:        struct window_mode_entry        *wme;
1.111     nicm      834:        char                            *sanitized, *msg, *line;
1.1       nicm      835:
1.111     nicm      836:        if (!parse) {
1.112   ! nicm      837:                utf8_stravisx(&msg, data, size, VIS_OCTAL|VIS_CSTYLE);
1.111     nicm      838:                log_debug("%s: %s", __func__, msg);
                    839:        } else {
                    840:                msg = EVBUFFER_DATA(evb);
                    841:                if (msg[size - 1] != '\0')
                    842:                        evbuffer_add(evb, "", 1);
                    843:        }
1.76      nicm      844:
1.111     nicm      845:        if (c == NULL)
                    846:                goto out;
1.1       nicm      847:
1.111     nicm      848:        if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
1.28      nicm      849:                if (~c->flags & CLIENT_UTF8) {
1.111     nicm      850:                        sanitized = utf8_sanitize(msg);
                    851:                        if (c->flags & CLIENT_CONTROL)
                    852:                                control_write(c, "%s", sanitized);
                    853:                        else
                    854:                                file_print(c, "%s\n", sanitized);
                    855:                        free(sanitized);
                    856:                } else {
                    857:                        if (c->flags & CLIENT_CONTROL)
                    858:                                control_write(c, "%s", msg);
                    859:                        else
                    860:                                file_print(c, "%s\n", msg);
1.76      nicm      861:                }
1.111     nicm      862:                goto out;
                    863:        }
                    864:
                    865:        wp = server_client_get_pane(c);
                    866:        wme = TAILQ_FIRST(&wp->modes);
                    867:        if (wme == NULL || wme->mode != &window_view_mode)
                    868:                window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
                    869:        if (parse) {
                    870:                do {
                    871:                        line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF);
                    872:                        if (line != NULL) {
                    873:                                window_copy_add(wp, 1, "%s", line);
                    874:                                free(line);
                    875:                        }
                    876:                } while (line != NULL);
                    877:
                    878:                size = EVBUFFER_LENGTH(evb);
                    879:                if (size != 0) {
                    880:                        line = EVBUFFER_DATA(evb);
                    881:                        window_copy_add(wp, 1, "%.*s", (int)size, line);
1.81      nicm      882:                }
1.111     nicm      883:        } else
1.110     nicm      884:                window_copy_add(wp, 0, "%s", msg);
1.1       nicm      885:
1.111     nicm      886: out:
                    887:        if (!parse)
                    888:                free(msg);
                    889:
                    890: }
                    891:
                    892: /* Show message from command. */
                    893: void
                    894: cmdq_print(struct cmdq_item *item, const char *fmt, ...)
                    895: {
                    896:        va_list          ap;
                    897:        struct evbuffer *evb;
                    898:
                    899:        evb = evbuffer_new();
                    900:        if (evb == NULL)
                    901:                fatalx("out of memory");
                    902:
                    903:        va_start(ap, fmt);
                    904:        evbuffer_add_vprintf(evb, fmt, ap);
                    905:        va_end(ap);
                    906:
                    907:        cmdq_print_data(item, 0, evb);
                    908:        evbuffer_free(evb);
1.1       nicm      909: }
                    910:
                    911: /* Show error from command. */
1.18      nicm      912: void
1.44      nicm      913: cmdq_error(struct cmdq_item *item, const char *fmt, ...)
1.1       nicm      914: {
1.44      nicm      915:        struct client   *c = item->client;
                    916:        struct cmd      *cmd = item->cmd;
1.1       nicm      917:        va_list          ap;
1.82      nicm      918:        char            *msg, *tmp;
                    919:        const char      *file;
                    920:        u_int            line;
1.1       nicm      921:
                    922:        va_start(ap, fmt);
1.76      nicm      923:        xvasprintf(&msg, fmt, ap);
1.1       nicm      924:        va_end(ap);
1.57      nicm      925:
                    926:        log_debug("%s: %s", __func__, msg);
1.1       nicm      927:
1.82      nicm      928:        if (c == NULL) {
                    929:                cmd_get_source(cmd, &file, &line);
                    930:                cfg_add_cause("%s:%u: %s", file, line, msg);
                    931:        } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
1.93      nicm      932:                server_add_message("%s message: %s", c->name, msg);
1.28      nicm      933:                if (~c->flags & CLIENT_UTF8) {
                    934:                        tmp = msg;
                    935:                        msg = utf8_sanitize(tmp);
                    936:                        free(tmp);
                    937:                }
1.79      nicm      938:                if (c->flags & CLIENT_CONTROL)
1.98      nicm      939:                        control_write(c, "%s", msg);
1.79      nicm      940:                else
                    941:                        file_error(c, "%s\n", msg);
1.13      nicm      942:                c->retval = 1;
1.1       nicm      943:        } else {
                    944:                *msg = toupper((u_char) *msg);
1.102     nicm      945:                status_message_set(c, -1, 1, 0, "%s", msg);
1.1       nicm      946:        }
                    947:
                    948:        free(msg);
                    949: }