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

1.115   ! nicm        1: /* $OpenBSD: cmd-queue.c,v 1.114 2023/02/05 21:15:32 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 *
1.115   ! nicm      240: cmdq_copy_state(struct cmdq_state *state, struct cmd_find_state *current)
1.88      nicm      241: {
1.115   ! nicm      242:        if (current != NULL)
        !           243:                return (cmdq_new_state(current, &state->event, state->flags));
1.88      nicm      244:        return (cmdq_new_state(&state->current, &state->event, state->flags));
                    245: }
                    246:
                    247: /* Free a state. */
                    248: void
                    249: cmdq_free_state(struct cmdq_state *state)
                    250: {
1.91      nicm      251:        if (--state->references != 0)
                    252:                return;
                    253:
                    254:        if (state->formats != NULL)
                    255:                format_free(state->formats);
                    256:        free(state);
1.88      nicm      257: }
                    258:
                    259: /* Add a format to command queue. */
                    260: void
                    261: cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...)
                    262: {
                    263:        va_list  ap;
                    264:        char    *value;
                    265:
                    266:        va_start(ap, fmt);
                    267:        xvasprintf(&value, fmt, ap);
                    268:        va_end(ap);
                    269:
                    270:        if (state->formats == NULL)
                    271:                state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
                    272:        format_add(state->formats, key, "%s", value);
                    273:
                    274:        free(value);
1.103     nicm      275: }
                    276:
                    277: /* Add formats to command queue. */
                    278: void
                    279: cmdq_add_formats(struct cmdq_state *state, struct format_tree *ft)
                    280: {
                    281:        if (state->formats == NULL)
                    282:                state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
                    283:        format_merge(state->formats, ft);
1.88      nicm      284: }
                    285:
1.83      nicm      286: /* Merge formats from item. */
                    287: void
                    288: cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
                    289: {
                    290:        const struct cmd_entry  *entry;
                    291:
                    292:        if (item->cmd != NULL) {
1.101     nicm      293:                entry = cmd_get_entry(item->cmd);
1.83      nicm      294:                format_add(ft, "command", "%s", entry->name);
                    295:        }
1.86      nicm      296:        if (item->state->formats != NULL)
                    297:                format_merge(ft, item->state->formats);
1.43      nicm      298: }
                    299:
                    300: /* Append an item. */
1.78      nicm      301: struct cmdq_item *
1.44      nicm      302: cmdq_append(struct client *c, struct cmdq_item *item)
1.43      nicm      303: {
1.44      nicm      304:        struct cmdq_list        *queue = cmdq_get(c);
                    305:        struct cmdq_item        *next;
1.43      nicm      306:
                    307:        do {
1.44      nicm      308:                next = item->next;
                    309:                item->next = NULL;
1.43      nicm      310:
                    311:                if (c != NULL)
                    312:                        c->references++;
1.44      nicm      313:                item->client = c;
1.43      nicm      314:
1.44      nicm      315:                item->queue = queue;
1.90      nicm      316:                TAILQ_INSERT_TAIL(&queue->list, item, entry);
1.64      nicm      317:                log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
1.43      nicm      318:
1.44      nicm      319:                item = next;
                    320:        } while (item != NULL);
1.90      nicm      321:        return (TAILQ_LAST(&queue->list, cmdq_item_list));
1.43      nicm      322: }
                    323:
                    324: /* Insert an item. */
1.78      nicm      325: struct cmdq_item *
1.44      nicm      326: cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
1.43      nicm      327: {
                    328:        struct client           *c = after->client;
1.44      nicm      329:        struct cmdq_list        *queue = after->queue;
                    330:        struct cmdq_item        *next;
1.43      nicm      331:
                    332:        do {
1.44      nicm      333:                next = item->next;
1.64      nicm      334:                item->next = after->next;
                    335:                after->next = item;
1.43      nicm      336:
                    337:                if (c != NULL)
                    338:                        c->references++;
1.44      nicm      339:                item->client = c;
1.43      nicm      340:
1.44      nicm      341:                item->queue = queue;
1.90      nicm      342:                TAILQ_INSERT_AFTER(&queue->list, after, item, entry);
1.64      nicm      343:                log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
                    344:                    item->name, after->name);
1.43      nicm      345:
1.65      nicm      346:                after = item;
1.44      nicm      347:                item = next;
                    348:        } while (item != NULL);
1.78      nicm      349:        return (after);
1.43      nicm      350: }
1.63      nicm      351:
                    352: /* Insert a hook. */
                    353: void
                    354: cmdq_insert_hook(struct session *s, struct cmdq_item *item,
1.88      nicm      355:     struct cmd_find_state *current, const char *fmt, ...)
1.63      nicm      356: {
1.88      nicm      357:        struct cmdq_state               *state = item->state;
1.92      nicm      358:        struct cmd                      *cmd = item->cmd;
                    359:        struct args                     *args = cmd_get_args(cmd);
1.104     nicm      360:        struct args_entry               *ae;
                    361:        struct args_value               *av;
1.63      nicm      362:        struct options                  *oo;
                    363:        va_list                          ap;
1.92      nicm      364:        char                            *name, tmp[32], flag, *arguments;
1.105     nicm      365:        u_int                            i;
1.92      nicm      366:        const char                      *value;
1.63      nicm      367:        struct cmdq_item                *new_item;
1.88      nicm      368:        struct cmdq_state               *new_state;
1.63      nicm      369:        struct options_entry            *o;
                    370:        struct options_array_item       *a;
                    371:        struct cmd_list                 *cmdlist;
                    372:
1.86      nicm      373:        if (item->state->flags & CMDQ_STATE_NOHOOKS)
1.63      nicm      374:                return;
                    375:        if (s == NULL)
                    376:                oo = global_s_options;
                    377:        else
                    378:                oo = s->options;
                    379:
                    380:        va_start(ap, fmt);
                    381:        xvasprintf(&name, fmt, ap);
                    382:        va_end(ap);
                    383:
                    384:        o = options_get(oo, name);
                    385:        if (o == NULL) {
                    386:                free(name);
                    387:                return;
                    388:        }
                    389:        log_debug("running hook %s (parent %p)", name, item);
                    390:
1.88      nicm      391:        /*
                    392:         * The hooks get a new state because they should not update the current
                    393:         * target or formats for any subsequent commands.
                    394:         */
                    395:        new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
                    396:        cmdq_add_format(new_state, "hook", "%s", name);
1.92      nicm      397:
                    398:        arguments = args_print(args);
                    399:        cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
                    400:        free(arguments);
                    401:
1.105     nicm      402:        for (i = 0; i < args_count(args); i++) {
1.92      nicm      403:                xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
1.105     nicm      404:                cmdq_add_format(new_state, tmp, "%s", args_string(args, i));
1.92      nicm      405:        }
1.104     nicm      406:        flag = args_first(args, &ae);
1.92      nicm      407:        while (flag != 0) {
                    408:                value = args_get(args, flag);
                    409:                if (value == NULL) {
                    410:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
                    411:                        cmdq_add_format(new_state, tmp, "1");
                    412:                } else {
                    413:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
                    414:                        cmdq_add_format(new_state, tmp, "%s", value);
                    415:                }
                    416:
                    417:                i = 0;
1.104     nicm      418:                av = args_first_value(args, flag);
                    419:                while (av != NULL) {
1.92      nicm      420:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
1.106     nicm      421:                        cmdq_add_format(new_state, tmp, "%s", av->string);
1.92      nicm      422:                        i++;
1.104     nicm      423:                        av = args_next_value(av);
1.92      nicm      424:                }
                    425:
1.104     nicm      426:                flag = args_next(&ae);
1.92      nicm      427:        }
1.88      nicm      428:
1.63      nicm      429:        a = options_array_first(o);
                    430:        while (a != NULL) {
                    431:                cmdlist = options_array_item_value(a)->cmdlist;
1.88      nicm      432:                if (cmdlist != NULL) {
                    433:                        new_item = cmdq_get_command(cmdlist, new_state);
                    434:                        if (item != NULL)
                    435:                                item = cmdq_insert_after(item, new_item);
                    436:                        else
                    437:                                item = cmdq_append(NULL, new_item);
1.63      nicm      438:                }
                    439:                a = options_array_next(a);
                    440:        }
                    441:
1.88      nicm      442:        cmdq_free_state(new_state);
1.63      nicm      443:        free(name);
1.74      nicm      444: }
                    445:
                    446: /* Continue processing command queue. */
                    447: void
                    448: cmdq_continue(struct cmdq_item *item)
                    449: {
                    450:        item->flags &= ~CMDQ_WAITING;
1.63      nicm      451: }
                    452:
1.43      nicm      453: /* Remove an item. */
                    454: static void
1.44      nicm      455: cmdq_remove(struct cmdq_item *item)
1.43      nicm      456: {
1.44      nicm      457:        if (item->client != NULL)
                    458:                server_client_unref(item->client);
1.62      nicm      459:        if (item->cmdlist != NULL)
1.44      nicm      460:                cmd_list_free(item->cmdlist);
1.88      nicm      461:        cmdq_free_state(item->state);
1.43      nicm      462:
1.90      nicm      463:        TAILQ_REMOVE(&item->queue->list, item, entry);
1.46      nicm      464:
1.64      nicm      465:        free(item->name);
1.44      nicm      466:        free(item);
1.43      nicm      467: }
                    468:
                    469: /* Remove all subsequent items that match this item's group. */
                    470: static void
1.44      nicm      471: cmdq_remove_group(struct cmdq_item *item)
1.43      nicm      472: {
1.44      nicm      473:        struct cmdq_item        *this, *next;
1.43      nicm      474:
1.69      nicm      475:        if (item->group == 0)
                    476:                return;
1.44      nicm      477:        this = TAILQ_NEXT(item, entry);
1.43      nicm      478:        while (this != NULL) {
                    479:                next = TAILQ_NEXT(this, entry);
1.44      nicm      480:                if (this->group == item->group)
1.43      nicm      481:                        cmdq_remove(this);
                    482:                this = next;
                    483:        }
                    484: }
                    485:
1.107     nicm      486: /* Empty command callback. */
                    487: static enum cmd_retval
                    488: cmdq_empty_command(__unused struct cmdq_item *item, __unused void *data)
                    489: {
                    490:        return (CMD_RETURN_NORMAL);
                    491: }
                    492:
1.43      nicm      493: /* Get a command for the command queue. */
1.44      nicm      494: struct cmdq_item *
1.88      nicm      495: cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
1.43      nicm      496: {
1.44      nicm      497:        struct cmdq_item        *item, *first = NULL, *last = NULL;
                    498:        struct cmd              *cmd;
1.82      nicm      499:        const struct cmd_entry  *entry;
1.88      nicm      500:        int                      created = 0;
1.49      nicm      501:
1.107     nicm      502:        if ((cmd = cmd_list_first(cmdlist)) == NULL)
                    503:                return (cmdq_get_callback(cmdq_empty_command, NULL));
                    504:
1.88      nicm      505:        if (state == NULL) {
                    506:                state = cmdq_new_state(NULL, NULL, 0);
                    507:                created = 1;
                    508:        }
                    509:
1.82      nicm      510:        while (cmd != NULL) {
                    511:                entry = cmd_get_entry(cmd);
1.43      nicm      512:
1.44      nicm      513:                item = xcalloc(1, sizeof *item);
1.82      nicm      514:                xasprintf(&item->name, "[%s/%p]", entry->name, item);
1.44      nicm      515:                item->type = CMDQ_COMMAND;
1.43      nicm      516:
1.88      nicm      517:                item->group = cmd_get_group(cmd);
                    518:                item->state = cmdq_link_state(state);
                    519:
1.44      nicm      520:                item->cmdlist = cmdlist;
                    521:                item->cmd = cmd;
1.68      nicm      522:
1.88      nicm      523:                cmdlist->references++;
1.68      nicm      524:                log_debug("%s: %s group %u", __func__, item->name, item->group);
1.43      nicm      525:
                    526:                if (first == NULL)
1.44      nicm      527:                        first = item;
1.43      nicm      528:                if (last != NULL)
1.44      nicm      529:                        last->next = item;
                    530:                last = item;
1.82      nicm      531:
1.88      nicm      532:                cmd = cmd_list_next(cmd);
1.43      nicm      533:        }
1.88      nicm      534:
                    535:        if (created)
                    536:                cmdq_free_state(state);
1.43      nicm      537:        return (first);
                    538: }
                    539:
1.54      nicm      540: /* Fill in flag for a command. */
                    541: static enum cmd_retval
                    542: cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
                    543:     const struct cmd_entry_flag *flag)
                    544: {
                    545:        const char      *value;
                    546:
                    547:        if (flag->flag == 0) {
1.95      nicm      548:                cmd_find_from_client(fs, item->target_client, 0);
1.54      nicm      549:                return (CMD_RETURN_NORMAL);
                    550:        }
                    551:
1.82      nicm      552:        value = args_get(cmd_get_args(item->cmd), flag->flag);
1.54      nicm      553:        if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
                    554:                cmd_find_clear_state(fs, 0);
                    555:                return (CMD_RETURN_ERROR);
                    556:        }
                    557:        return (CMD_RETURN_NORMAL);
                    558: }
                    559:
1.93      nicm      560: /* Add message with command. */
                    561: static void
                    562: cmdq_add_message(struct cmdq_item *item)
                    563: {
                    564:        struct client           *c = item->client;
                    565:        struct cmdq_state       *state = item->state;
1.108     nicm      566:        const char              *key;
1.93      nicm      567:        char                    *tmp;
1.108     nicm      568:        uid_t                    uid;
                    569:        struct passwd           *pw;
                    570:        char                    *user = NULL;
1.93      nicm      571:
                    572:        tmp = cmd_print(item->cmd);
                    573:        if (c != NULL) {
1.108     nicm      574:                uid = proc_get_peer_uid(c->peer);
                    575:                if (uid != (uid_t)-1 && uid != getuid()) {
                    576:                        if ((pw = getpwuid(uid)) != NULL)
                    577:                                xasprintf(&user, "[%s]", pw->pw_name);
                    578:                        else
                    579:                                user = xstrdup("[unknown]");
                    580:                } else
                    581:                        user = xstrdup("");
1.93      nicm      582:                if (c->session != NULL && state->event.key != KEYC_NONE) {
1.97      nicm      583:                        key = key_string_lookup_key(state->event.key, 0);
1.108     nicm      584:                        server_add_message("%s%s key %s: %s", c->name, user,
                    585:                            key, tmp);
                    586:                } else {
                    587:                        server_add_message("%s%s command: %s", c->name, user,
                    588:                            tmp);
                    589:                }
                    590:                free(user);
1.93      nicm      591:        } else
                    592:                server_add_message("command: %s", tmp);
                    593:        free(tmp);
                    594: }
                    595:
1.43      nicm      596: /* Fire command on command queue. */
                    597: static enum cmd_retval
1.44      nicm      598: cmdq_fire_command(struct cmdq_item *item)
1.43      nicm      599: {
1.89      nicm      600:        const char              *name = cmdq_name(item->client);
1.86      nicm      601:        struct cmdq_state       *state = item->state;
1.44      nicm      602:        struct cmd              *cmd = item->cmd;
1.89      nicm      603:        struct args             *args = cmd_get_args(cmd);
1.82      nicm      604:        const struct cmd_entry  *entry = cmd_get_entry(cmd);
1.89      nicm      605:        struct client           *tc, *saved = item->client;
1.43      nicm      606:        enum cmd_retval          retval;
                    607:        struct cmd_find_state   *fsp, fs;
1.89      nicm      608:        int                      flags, quiet = 0;
1.72      nicm      609:        char                    *tmp;
                    610:
1.93      nicm      611:        if (cfg_finished)
                    612:                cmdq_add_message(item);
1.72      nicm      613:        if (log_get_level() > 1) {
                    614:                tmp = cmd_print(cmd);
                    615:                log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
                    616:                free(tmp);
                    617:        }
1.43      nicm      618:
1.86      nicm      619:        flags = !!(state->flags & CMDQ_STATE_CONTROL);
1.44      nicm      620:        cmdq_guard(item, "begin", flags);
1.43      nicm      621:
1.53      nicm      622:        if (item->client == NULL)
                    623:                item->client = cmd_find_client(item, NULL, 1);
1.89      nicm      624:
                    625:        if (entry->flags & CMD_CLIENT_CANFAIL)
                    626:                quiet = 1;
                    627:        if (entry->flags & CMD_CLIENT_CFLAG) {
                    628:                tc = cmd_find_client(item, args_get(args, 'c'), quiet);
                    629:                if (tc == NULL && !quiet) {
                    630:                        retval = CMD_RETURN_ERROR;
                    631:                        goto out;
                    632:                }
                    633:        } else if (entry->flags & CMD_CLIENT_TFLAG) {
                    634:                tc = cmd_find_client(item, args_get(args, 't'), quiet);
                    635:                if (tc == NULL && !quiet) {
                    636:                        retval = CMD_RETURN_ERROR;
                    637:                        goto out;
                    638:                }
                    639:        } else
                    640:                tc = cmd_find_client(item, NULL, 1);
                    641:        item->target_client = tc;
                    642:
1.54      nicm      643:        retval = cmdq_find_flag(item, &item->source, &entry->source);
                    644:        if (retval == CMD_RETURN_ERROR)
                    645:                goto out;
                    646:        retval = cmdq_find_flag(item, &item->target, &entry->target);
                    647:        if (retval == CMD_RETURN_ERROR)
1.43      nicm      648:                goto out;
1.89      nicm      649:
1.54      nicm      650:        retval = entry->exec(cmd, item);
1.43      nicm      651:        if (retval == CMD_RETURN_ERROR)
                    652:                goto out;
                    653:
1.54      nicm      654:        if (entry->flags & CMD_AFTERHOOK) {
                    655:                if (cmd_find_valid_state(&item->target))
                    656:                        fsp = &item->target;
1.86      nicm      657:                else if (cmd_find_valid_state(&item->state->current))
                    658:                        fsp = &item->state->current;
1.58      nicm      659:                else if (cmd_find_from_client(&fs, item->client, 0) == 0)
1.43      nicm      660:                        fsp = &fs;
1.51      nicm      661:                else
                    662:                        goto out;
1.63      nicm      663:                cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
1.43      nicm      664:        }
                    665:
                    666: out:
1.89      nicm      667:        item->client = saved;
1.43      nicm      668:        if (retval == CMD_RETURN_ERROR)
1.44      nicm      669:                cmdq_guard(item, "error", flags);
1.43      nicm      670:        else
1.44      nicm      671:                cmdq_guard(item, "end", flags);
1.43      nicm      672:        return (retval);
                    673: }
                    674:
                    675: /* Get a callback for the command queue. */
1.44      nicm      676: struct cmdq_item *
1.46      nicm      677: cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
1.1       nicm      678: {
1.44      nicm      679:        struct cmdq_item        *item;
1.1       nicm      680:
1.44      nicm      681:        item = xcalloc(1, sizeof *item);
1.64      nicm      682:        xasprintf(&item->name, "[%s/%p]", name, item);
1.44      nicm      683:        item->type = CMDQ_CALLBACK;
1.88      nicm      684:
1.44      nicm      685:        item->group = 0;
1.88      nicm      686:        item->state = cmdq_new_state(NULL, NULL, 0);
1.1       nicm      687:
1.44      nicm      688:        item->cb = cb;
                    689:        item->data = data;
1.1       nicm      690:
1.44      nicm      691:        return (item);
1.67      nicm      692: }
                    693:
                    694: /* Generic error callback. */
                    695: static enum cmd_retval
                    696: cmdq_error_callback(struct cmdq_item *item, void *data)
                    697: {
                    698:        char    *error = data;
                    699:
                    700:        cmdq_error(item, "%s", error);
                    701:        free(error);
                    702:
                    703:        return (CMD_RETURN_NORMAL);
                    704: }
                    705:
                    706: /* Get an error callback for the command queue. */
                    707: struct cmdq_item *
                    708: cmdq_get_error(const char *error)
                    709: {
                    710:        return (cmdq_get_callback(cmdq_error_callback, xstrdup(error)));
1.43      nicm      711: }
1.1       nicm      712:
1.43      nicm      713: /* Fire callback on callback queue. */
                    714: static enum cmd_retval
1.44      nicm      715: cmdq_fire_callback(struct cmdq_item *item)
1.43      nicm      716: {
1.44      nicm      717:        return (item->cb(item, item->data));
1.45      nicm      718: }
1.33      nicm      719:
1.43      nicm      720: /* Process next item on command queue. */
                    721: u_int
                    722: cmdq_next(struct client *c)
                    723: {
1.44      nicm      724:        struct cmdq_list        *queue = cmdq_get(c);
1.43      nicm      725:        const char              *name = cmdq_name(c);
1.44      nicm      726:        struct cmdq_item        *item;
1.43      nicm      727:        enum cmd_retval          retval;
                    728:        u_int                    items = 0;
                    729:        static u_int             number;
1.1       nicm      730:
1.90      nicm      731:        if (TAILQ_EMPTY(&queue->list)) {
1.43      nicm      732:                log_debug("%s %s: empty", __func__, name);
1.25      nicm      733:                return (0);
                    734:        }
1.90      nicm      735:        if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) {
1.43      nicm      736:                log_debug("%s %s: waiting", __func__, name);
                    737:                return (0);
                    738:        }
                    739:
                    740:        log_debug("%s %s: enter", __func__, name);
                    741:        for (;;) {
1.90      nicm      742:                item = queue->item = TAILQ_FIRST(&queue->list);
1.44      nicm      743:                if (item == NULL)
1.43      nicm      744:                        break;
1.46      nicm      745:                log_debug("%s %s: %s (%d), flags %x", __func__, name,
                    746:                    item->name, item->type, item->flags);
1.43      nicm      747:
                    748:                /*
                    749:                 * Any item with the waiting flag set waits until an external
                    750:                 * event clears the flag (for example, a job - look at
                    751:                 * run-shell).
                    752:                 */
1.44      nicm      753:                if (item->flags & CMDQ_WAITING)
1.43      nicm      754:                        goto waiting;
                    755:
                    756:                /*
                    757:                 * Items are only fired once, once the fired flag is set, a
                    758:                 * waiting flag can only be cleared by an external event.
                    759:                 */
1.44      nicm      760:                if (~item->flags & CMDQ_FIRED) {
                    761:                        item->time = time(NULL);
                    762:                        item->number = ++number;
1.43      nicm      763:
1.50      nicm      764:                        switch (item->type) {
1.44      nicm      765:                        case CMDQ_COMMAND:
                    766:                                retval = cmdq_fire_command(item);
1.43      nicm      767:
                    768:                                /*
                    769:                                 * If a command returns an error, remove any
                    770:                                 * subsequent commands in the same group.
                    771:                                 */
                    772:                                if (retval == CMD_RETURN_ERROR)
1.44      nicm      773:                                        cmdq_remove_group(item);
1.43      nicm      774:                                break;
1.44      nicm      775:                        case CMDQ_CALLBACK:
                    776:                                retval = cmdq_fire_callback(item);
1.43      nicm      777:                                break;
                    778:                        default:
                    779:                                retval = CMD_RETURN_ERROR;
                    780:                                break;
                    781:                        }
1.44      nicm      782:                        item->flags |= CMDQ_FIRED;
1.43      nicm      783:
                    784:                        if (retval == CMD_RETURN_WAIT) {
1.44      nicm      785:                                item->flags |= CMDQ_WAITING;
1.43      nicm      786:                                goto waiting;
                    787:                        }
                    788:                        items++;
                    789:                }
1.44      nicm      790:                cmdq_remove(item);
1.43      nicm      791:        }
1.90      nicm      792:        queue->item = NULL;
1.43      nicm      793:
                    794:        log_debug("%s %s: exit (empty)", __func__, name);
                    795:        return (items);
1.1       nicm      796:
1.43      nicm      797: waiting:
                    798:        log_debug("%s %s: exit (wait)", __func__, name);
                    799:        return (items);
1.90      nicm      800: }
                    801:
                    802: /* Get running item if any. */
                    803: struct cmdq_item *
                    804: cmdq_running(struct client *c)
                    805: {
                    806:        struct cmdq_list        *queue = cmdq_get(c);
                    807:
1.100     nicm      808:        if (queue->item == NULL)
                    809:         return (NULL);
                    810:     if (queue->item->flags & CMDQ_WAITING)
                    811:         return (NULL);
                    812:     return (queue->item);
1.43      nicm      813: }
                    814:
                    815: /* Print a guard line. */
                    816: void
1.44      nicm      817: cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
1.43      nicm      818: {
1.44      nicm      819:        struct client   *c = item->client;
1.76      nicm      820:        long             t = item->time;
                    821:        u_int            number = item->number;
1.43      nicm      822:
1.76      nicm      823:        if (c != NULL && (c->flags & CLIENT_CONTROL))
1.98      nicm      824:                control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
1.1       nicm      825: }
                    826:
                    827: /* Show message from command. */
1.18      nicm      828: void
1.111     nicm      829: cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb)
1.1       nicm      830: {
1.114     nicm      831:        server_client_print(item->client, parse, evb);
1.111     nicm      832: }
                    833:
                    834: /* Show message from command. */
                    835: void
                    836: cmdq_print(struct cmdq_item *item, const char *fmt, ...)
                    837: {
                    838:        va_list          ap;
                    839:        struct evbuffer *evb;
                    840:
                    841:        evb = evbuffer_new();
                    842:        if (evb == NULL)
                    843:                fatalx("out of memory");
                    844:
                    845:        va_start(ap, fmt);
                    846:        evbuffer_add_vprintf(evb, fmt, ap);
                    847:        va_end(ap);
                    848:
                    849:        cmdq_print_data(item, 0, evb);
                    850:        evbuffer_free(evb);
1.1       nicm      851: }
                    852:
                    853: /* Show error from command. */
1.18      nicm      854: void
1.44      nicm      855: cmdq_error(struct cmdq_item *item, const char *fmt, ...)
1.1       nicm      856: {
1.44      nicm      857:        struct client   *c = item->client;
                    858:        struct cmd      *cmd = item->cmd;
1.1       nicm      859:        va_list          ap;
1.82      nicm      860:        char            *msg, *tmp;
                    861:        const char      *file;
                    862:        u_int            line;
1.1       nicm      863:
                    864:        va_start(ap, fmt);
1.76      nicm      865:        xvasprintf(&msg, fmt, ap);
1.1       nicm      866:        va_end(ap);
1.57      nicm      867:
                    868:        log_debug("%s: %s", __func__, msg);
1.1       nicm      869:
1.82      nicm      870:        if (c == NULL) {
                    871:                cmd_get_source(cmd, &file, &line);
                    872:                cfg_add_cause("%s:%u: %s", file, line, msg);
                    873:        } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
1.93      nicm      874:                server_add_message("%s message: %s", c->name, msg);
1.28      nicm      875:                if (~c->flags & CLIENT_UTF8) {
                    876:                        tmp = msg;
                    877:                        msg = utf8_sanitize(tmp);
                    878:                        free(tmp);
                    879:                }
1.79      nicm      880:                if (c->flags & CLIENT_CONTROL)
1.98      nicm      881:                        control_write(c, "%s", msg);
1.79      nicm      882:                else
                    883:                        file_error(c, "%s\n", msg);
1.13      nicm      884:                c->retval = 1;
1.1       nicm      885:        } else {
                    886:                *msg = toupper((u_char) *msg);
1.102     nicm      887:                status_message_set(c, -1, 1, 0, "%s", msg);
1.1       nicm      888:        }
                    889:
                    890:        free(msg);
                    891: }