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

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