15494e770Schristos /* $OpenBSD$ */ 2928fc495Schristos 3928fc495Schristos /* 4ed4e6cd4Schristos * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com> 5928fc495Schristos * 6928fc495Schristos * Permission to use, copy, modify, and distribute this software for any 7928fc495Schristos * purpose with or without fee is hereby granted, provided that the above 8928fc495Schristos * copyright notice and this permission notice appear in all copies. 9928fc495Schristos * 10928fc495Schristos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11928fc495Schristos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12928fc495Schristos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13928fc495Schristos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14928fc495Schristos * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15928fc495Schristos * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16928fc495Schristos * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17928fc495Schristos */ 18928fc495Schristos 19928fc495Schristos #include <sys/types.h> 20928fc495Schristos 21928fc495Schristos #include <ctype.h> 2246548964Swiz #include <pwd.h> 23928fc495Schristos #include <stdlib.h> 245494e770Schristos #include <string.h> 25928fc495Schristos #include <time.h> 2646548964Swiz #include <unistd.h> 27928fc495Schristos 28928fc495Schristos #include "tmux.h" 29928fc495Schristos 30e271dbb8Schristos /* Command queue flags. */ 31e271dbb8Schristos #define CMDQ_FIRED 0x1 32e271dbb8Schristos #define CMDQ_WAITING 0x2 33e271dbb8Schristos 34e271dbb8Schristos /* Command queue item type. */ 35e271dbb8Schristos enum cmdq_type { 36e271dbb8Schristos CMDQ_COMMAND, 37e271dbb8Schristos CMDQ_CALLBACK, 38e271dbb8Schristos }; 39e271dbb8Schristos 40e271dbb8Schristos /* Command queue item. */ 41e271dbb8Schristos struct cmdq_item { 42e271dbb8Schristos char *name; 43e271dbb8Schristos struct cmdq_list *queue; 44e271dbb8Schristos struct cmdq_item *next; 45e271dbb8Schristos 46e271dbb8Schristos struct client *client; 47e271dbb8Schristos struct client *target_client; 48e271dbb8Schristos 49e271dbb8Schristos enum cmdq_type type; 50e271dbb8Schristos u_int group; 51e271dbb8Schristos 52e271dbb8Schristos u_int number; 53e271dbb8Schristos time_t time; 54e271dbb8Schristos 55e271dbb8Schristos int flags; 56e271dbb8Schristos 57e271dbb8Schristos struct cmdq_state *state; 58e271dbb8Schristos struct cmd_find_state source; 59e271dbb8Schristos struct cmd_find_state target; 60e271dbb8Schristos 61e271dbb8Schristos struct cmd_list *cmdlist; 62e271dbb8Schristos struct cmd *cmd; 63e271dbb8Schristos 64e271dbb8Schristos cmdq_cb cb; 65e271dbb8Schristos void *data; 66e271dbb8Schristos 67e271dbb8Schristos TAILQ_ENTRY(cmdq_item) entry; 68e271dbb8Schristos }; 69e271dbb8Schristos TAILQ_HEAD(cmdq_item_list, cmdq_item); 70e271dbb8Schristos 71e271dbb8Schristos /* 72e271dbb8Schristos * Command queue state. This is the context for commands on the command queue. 73e271dbb8Schristos * It holds information about how the commands were fired (the key and flags), 74e271dbb8Schristos * any additional formats for the commands, and the current default target. 75e271dbb8Schristos * Multiple commands can share the same state and a command may update the 76e271dbb8Schristos * default target. 77e271dbb8Schristos */ 78e271dbb8Schristos struct cmdq_state { 79e271dbb8Schristos int references; 80e271dbb8Schristos int flags; 81e271dbb8Schristos 82e271dbb8Schristos struct format_tree *formats; 83e271dbb8Schristos 84e271dbb8Schristos struct key_event event; 85e271dbb8Schristos struct cmd_find_state current; 86e271dbb8Schristos }; 87e271dbb8Schristos 88e271dbb8Schristos /* Command queue. */ 89e271dbb8Schristos struct cmdq_list { 90e271dbb8Schristos struct cmdq_item *item; 91e271dbb8Schristos struct cmdq_item_list list; 92e271dbb8Schristos }; 935494e770Schristos 944e179ddaSchristos /* Get command queue name. */ 954e179ddaSchristos static const char * 964e179ddaSchristos cmdq_name(struct client *c) 97928fc495Schristos { 9830744affSchristos static char s[256]; 99928fc495Schristos 1004e179ddaSchristos if (c == NULL) 1014e179ddaSchristos return ("<global>"); 10230744affSchristos if (c->name != NULL) 10330744affSchristos xsnprintf(s, sizeof s, "<%s>", c->name); 10430744affSchristos else 1054e179ddaSchristos xsnprintf(s, sizeof s, "<%p>", c); 1064e179ddaSchristos return (s); 107928fc495Schristos } 108928fc495Schristos 1094e179ddaSchristos /* Get command queue from client. */ 1104e179ddaSchristos static struct cmdq_list * 1114e179ddaSchristos cmdq_get(struct client *c) 112928fc495Schristos { 113e271dbb8Schristos static struct cmdq_list *global_queue; 114e271dbb8Schristos 115e271dbb8Schristos if (c == NULL) { 116e271dbb8Schristos if (global_queue == NULL) 117e271dbb8Schristos global_queue = cmdq_new(); 118e271dbb8Schristos return (global_queue); 119e271dbb8Schristos } 120e271dbb8Schristos return (c->queue); 121e271dbb8Schristos } 122e271dbb8Schristos 123e271dbb8Schristos /* Create a queue. */ 124e271dbb8Schristos struct cmdq_list * 125e271dbb8Schristos cmdq_new(void) 126e271dbb8Schristos { 127e271dbb8Schristos struct cmdq_list *queue; 128e271dbb8Schristos 129e271dbb8Schristos queue = xcalloc(1, sizeof *queue); 130e271dbb8Schristos TAILQ_INIT (&queue->list); 131e271dbb8Schristos return (queue); 132e271dbb8Schristos } 133e271dbb8Schristos 134e271dbb8Schristos /* Free a queue. */ 135e271dbb8Schristos void 136e271dbb8Schristos cmdq_free(struct cmdq_list *queue) 137e271dbb8Schristos { 138e271dbb8Schristos if (!TAILQ_EMPTY(&queue->list)) 139e271dbb8Schristos fatalx("queue not empty"); 140e271dbb8Schristos free(queue); 141e271dbb8Schristos } 142e271dbb8Schristos 143e271dbb8Schristos /* Get item name. */ 144e271dbb8Schristos const char * 145e271dbb8Schristos cmdq_get_name(struct cmdq_item *item) 146e271dbb8Schristos { 147e271dbb8Schristos return (item->name); 148e271dbb8Schristos } 149e271dbb8Schristos 150e271dbb8Schristos /* Get item client. */ 151e271dbb8Schristos struct client * 152e271dbb8Schristos cmdq_get_client(struct cmdq_item *item) 153e271dbb8Schristos { 154e271dbb8Schristos return (item->client); 155e271dbb8Schristos } 156e271dbb8Schristos 157e271dbb8Schristos /* Get item target client. */ 158e271dbb8Schristos struct client * 159e271dbb8Schristos cmdq_get_target_client(struct cmdq_item *item) 160e271dbb8Schristos { 161e271dbb8Schristos return (item->target_client); 162e271dbb8Schristos } 163e271dbb8Schristos 164e271dbb8Schristos /* Get item state. */ 165e271dbb8Schristos struct cmdq_state * 166e271dbb8Schristos cmdq_get_state(struct cmdq_item *item) 167e271dbb8Schristos { 168e271dbb8Schristos return (item->state); 169e271dbb8Schristos } 170e271dbb8Schristos 171e271dbb8Schristos /* Get item target. */ 172e271dbb8Schristos struct cmd_find_state * 173e271dbb8Schristos cmdq_get_target(struct cmdq_item *item) 174e271dbb8Schristos { 175e271dbb8Schristos return (&item->target); 176e271dbb8Schristos } 177e271dbb8Schristos 178e271dbb8Schristos /* Get item source. */ 179e271dbb8Schristos struct cmd_find_state * 180e271dbb8Schristos cmdq_get_source(struct cmdq_item *item) 181e271dbb8Schristos { 182e271dbb8Schristos return (&item->source); 183e271dbb8Schristos } 184e271dbb8Schristos 185e271dbb8Schristos /* Get state event. */ 186e271dbb8Schristos struct key_event * 187e271dbb8Schristos cmdq_get_event(struct cmdq_item *item) 188e271dbb8Schristos { 189e271dbb8Schristos return (&item->state->event); 190e271dbb8Schristos } 191e271dbb8Schristos 192e271dbb8Schristos /* Get state current target. */ 193e271dbb8Schristos struct cmd_find_state * 194e271dbb8Schristos cmdq_get_current(struct cmdq_item *item) 195e271dbb8Schristos { 196e271dbb8Schristos return (&item->state->current); 197e271dbb8Schristos } 198e271dbb8Schristos 199e271dbb8Schristos /* Get state flags. */ 200e271dbb8Schristos int 201e271dbb8Schristos cmdq_get_flags(struct cmdq_item *item) 202e271dbb8Schristos { 203e271dbb8Schristos return (item->state->flags); 204e271dbb8Schristos } 205e271dbb8Schristos 206e271dbb8Schristos /* Create a new state. */ 207e271dbb8Schristos struct cmdq_state * 208e271dbb8Schristos cmdq_new_state(struct cmd_find_state *current, struct key_event *event, 209e271dbb8Schristos int flags) 210e271dbb8Schristos { 211e271dbb8Schristos struct cmdq_state *state; 212e271dbb8Schristos 213e271dbb8Schristos state = xcalloc(1, sizeof *state); 214e271dbb8Schristos state->references = 1; 215e271dbb8Schristos state->flags = flags; 216e271dbb8Schristos 217e271dbb8Schristos if (event != NULL) 218e271dbb8Schristos memcpy(&state->event, event, sizeof state->event); 219e271dbb8Schristos else 220e271dbb8Schristos state->event.key = KEYC_NONE; 221e271dbb8Schristos if (current != NULL && cmd_find_valid_state(current)) 222e271dbb8Schristos cmd_find_copy_state(&state->current, current); 223e271dbb8Schristos else 224e271dbb8Schristos cmd_find_clear_state(&state->current, 0); 225e271dbb8Schristos 226e271dbb8Schristos return (state); 227e271dbb8Schristos } 228e271dbb8Schristos 229e271dbb8Schristos /* Add a reference to a state. */ 230e271dbb8Schristos struct cmdq_state * 231e271dbb8Schristos cmdq_link_state(struct cmdq_state *state) 232e271dbb8Schristos { 233e271dbb8Schristos state->references++; 234e271dbb8Schristos return (state); 235e271dbb8Schristos } 236e271dbb8Schristos 237e271dbb8Schristos /* Make a copy of a state. */ 238e271dbb8Schristos struct cmdq_state * 239f844e94eSwiz cmdq_copy_state(struct cmdq_state *state, struct cmd_find_state *current) 240e271dbb8Schristos { 241f844e94eSwiz if (current != NULL) 242f844e94eSwiz return (cmdq_new_state(current, &state->event, state->flags)); 243e271dbb8Schristos return (cmdq_new_state(&state->current, &state->event, state->flags)); 244e271dbb8Schristos } 245e271dbb8Schristos 246e271dbb8Schristos /* Free a state. */ 247e271dbb8Schristos void 248e271dbb8Schristos cmdq_free_state(struct cmdq_state *state) 249e271dbb8Schristos { 250e271dbb8Schristos if (--state->references != 0) 251e271dbb8Schristos return; 252e271dbb8Schristos 253e271dbb8Schristos if (state->formats != NULL) 254e271dbb8Schristos format_free(state->formats); 255e271dbb8Schristos free(state); 256e271dbb8Schristos } 257e271dbb8Schristos 258e271dbb8Schristos /* Add a format to command queue. */ 259e271dbb8Schristos void 260e271dbb8Schristos cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...) 261e271dbb8Schristos { 262e271dbb8Schristos va_list ap; 263e271dbb8Schristos char *value; 264e271dbb8Schristos 265e271dbb8Schristos va_start(ap, fmt); 266e271dbb8Schristos xvasprintf(&value, fmt, ap); 267e271dbb8Schristos va_end(ap); 268e271dbb8Schristos 269e271dbb8Schristos if (state->formats == NULL) 270e271dbb8Schristos state->formats = format_create(NULL, NULL, FORMAT_NONE, 0); 271e271dbb8Schristos format_add(state->formats, key, "%s", value); 272e271dbb8Schristos 273e271dbb8Schristos free(value); 274e271dbb8Schristos } 275e271dbb8Schristos 27646548964Swiz /* Add formats to command queue. */ 27746548964Swiz void 27846548964Swiz cmdq_add_formats(struct cmdq_state *state, struct format_tree *ft) 27946548964Swiz { 28046548964Swiz if (state->formats == NULL) 28146548964Swiz state->formats = format_create(NULL, NULL, FORMAT_NONE, 0); 28246548964Swiz format_merge(state->formats, ft); 28346548964Swiz } 28446548964Swiz 285e271dbb8Schristos /* Merge formats from item. */ 286e271dbb8Schristos void 287e271dbb8Schristos cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft) 288e271dbb8Schristos { 289e271dbb8Schristos const struct cmd_entry *entry; 290e271dbb8Schristos 291e271dbb8Schristos if (item->cmd != NULL) { 292e271dbb8Schristos entry = cmd_get_entry(item->cmd); 293e271dbb8Schristos format_add(ft, "command", "%s", entry->name); 294e271dbb8Schristos } 295e271dbb8Schristos if (item->state->formats != NULL) 296e271dbb8Schristos format_merge(ft, item->state->formats); 2974e179ddaSchristos } 2984e179ddaSchristos 2994e179ddaSchristos /* Append an item. */ 30068e6ba84Schristos struct cmdq_item * 3014e179ddaSchristos cmdq_append(struct client *c, struct cmdq_item *item) 3024e179ddaSchristos { 3034e179ddaSchristos struct cmdq_list *queue = cmdq_get(c); 3044e179ddaSchristos struct cmdq_item *next; 3054e179ddaSchristos 3064e179ddaSchristos do { 3074e179ddaSchristos next = item->next; 3084e179ddaSchristos item->next = NULL; 3094e179ddaSchristos 3104e179ddaSchristos if (c != NULL) 3114e179ddaSchristos c->references++; 3124e179ddaSchristos item->client = c; 3134e179ddaSchristos 3144e179ddaSchristos item->queue = queue; 315e271dbb8Schristos TAILQ_INSERT_TAIL(&queue->list, item, entry); 31630744affSchristos log_debug("%s %s: %s", __func__, cmdq_name(c), item->name); 3174e179ddaSchristos 3184e179ddaSchristos item = next; 3194e179ddaSchristos } while (item != NULL); 320e271dbb8Schristos return (TAILQ_LAST(&queue->list, cmdq_item_list)); 3214e179ddaSchristos } 3224e179ddaSchristos 3234e179ddaSchristos /* Insert an item. */ 32468e6ba84Schristos struct cmdq_item * 3254e179ddaSchristos cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) 3264e179ddaSchristos { 3274e179ddaSchristos struct client *c = after->client; 3284e179ddaSchristos struct cmdq_list *queue = after->queue; 3294e179ddaSchristos struct cmdq_item *next; 3304e179ddaSchristos 3314e179ddaSchristos do { 3324e179ddaSchristos next = item->next; 33330744affSchristos item->next = after->next; 33430744affSchristos after->next = item; 3354e179ddaSchristos 3364e179ddaSchristos if (c != NULL) 3374e179ddaSchristos c->references++; 3384e179ddaSchristos item->client = c; 3394e179ddaSchristos 3404e179ddaSchristos item->queue = queue; 341e271dbb8Schristos TAILQ_INSERT_AFTER(&queue->list, after, item, entry); 34230744affSchristos log_debug("%s %s: %s after %s", __func__, cmdq_name(c), 34330744affSchristos item->name, after->name); 3444e179ddaSchristos 34530744affSchristos after = item; 3464e179ddaSchristos item = next; 3474e179ddaSchristos } while (item != NULL); 34868e6ba84Schristos return (after); 3494e179ddaSchristos } 3504e179ddaSchristos 35130744affSchristos /* Insert a hook. */ 35230744affSchristos void 35330744affSchristos cmdq_insert_hook(struct session *s, struct cmdq_item *item, 354e271dbb8Schristos struct cmd_find_state *current, const char *fmt, ...) 35530744affSchristos { 356e271dbb8Schristos struct cmdq_state *state = item->state; 357e271dbb8Schristos struct cmd *cmd = item->cmd; 358e271dbb8Schristos struct args *args = cmd_get_args(cmd); 35946548964Swiz struct args_entry *ae; 36046548964Swiz struct args_value *av; 36130744affSchristos struct options *oo; 36230744affSchristos va_list ap; 363e271dbb8Schristos char *name, tmp[32], flag, *arguments; 36446548964Swiz u_int i; 365e271dbb8Schristos const char *value; 36630744affSchristos struct cmdq_item *new_item; 367e271dbb8Schristos struct cmdq_state *new_state; 36830744affSchristos struct options_entry *o; 36930744affSchristos struct options_array_item *a; 37030744affSchristos struct cmd_list *cmdlist; 37130744affSchristos 372e271dbb8Schristos if (item->state->flags & CMDQ_STATE_NOHOOKS) 37330744affSchristos return; 37430744affSchristos if (s == NULL) 37530744affSchristos oo = global_s_options; 37630744affSchristos else 37730744affSchristos oo = s->options; 37830744affSchristos 37930744affSchristos va_start(ap, fmt); 38030744affSchristos xvasprintf(&name, fmt, ap); 38130744affSchristos va_end(ap); 38230744affSchristos 38330744affSchristos o = options_get(oo, name); 38430744affSchristos if (o == NULL) { 38530744affSchristos free(name); 38630744affSchristos return; 38730744affSchristos } 38830744affSchristos log_debug("running hook %s (parent %p)", name, item); 38930744affSchristos 390e271dbb8Schristos /* 391e271dbb8Schristos * The hooks get a new state because they should not update the current 392e271dbb8Schristos * target or formats for any subsequent commands. 393e271dbb8Schristos */ 394e271dbb8Schristos new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS); 395e271dbb8Schristos cmdq_add_format(new_state, "hook", "%s", name); 396e271dbb8Schristos 397e271dbb8Schristos arguments = args_print(args); 398e271dbb8Schristos cmdq_add_format(new_state, "hook_arguments", "%s", arguments); 399e271dbb8Schristos free(arguments); 400e271dbb8Schristos 40146548964Swiz for (i = 0; i < args_count(args); i++) { 402e271dbb8Schristos xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i); 40346548964Swiz cmdq_add_format(new_state, tmp, "%s", args_string(args, i)); 404e271dbb8Schristos } 40546548964Swiz flag = args_first(args, &ae); 406e271dbb8Schristos while (flag != 0) { 407e271dbb8Schristos value = args_get(args, flag); 408e271dbb8Schristos if (value == NULL) { 409e271dbb8Schristos xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag); 410e271dbb8Schristos cmdq_add_format(new_state, tmp, "1"); 411e271dbb8Schristos } else { 412e271dbb8Schristos xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag); 413e271dbb8Schristos cmdq_add_format(new_state, tmp, "%s", value); 414e271dbb8Schristos } 415e271dbb8Schristos 416e271dbb8Schristos i = 0; 41746548964Swiz av = args_first_value(args, flag); 41846548964Swiz while (av != NULL) { 419e271dbb8Schristos xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i); 42046548964Swiz cmdq_add_format(new_state, tmp, "%s", av->string); 421e271dbb8Schristos i++; 42246548964Swiz av = args_next_value(av); 423e271dbb8Schristos } 424e271dbb8Schristos 42546548964Swiz flag = args_next(&ae); 426e271dbb8Schristos } 427e271dbb8Schristos 42830744affSchristos a = options_array_first(o); 42930744affSchristos while (a != NULL) { 43030744affSchristos cmdlist = options_array_item_value(a)->cmdlist; 431e271dbb8Schristos if (cmdlist != NULL) { 432e271dbb8Schristos new_item = cmdq_get_command(cmdlist, new_state); 43368e6ba84Schristos if (item != NULL) 43468e6ba84Schristos item = cmdq_insert_after(item, new_item); 43568e6ba84Schristos else 43668e6ba84Schristos item = cmdq_append(NULL, new_item); 437e271dbb8Schristos } 43830744affSchristos a = options_array_next(a); 43930744affSchristos } 44030744affSchristos 441e271dbb8Schristos cmdq_free_state(new_state); 44230744affSchristos free(name); 44330744affSchristos } 44430744affSchristos 44530744affSchristos /* Continue processing command queue. */ 44630744affSchristos void 44730744affSchristos cmdq_continue(struct cmdq_item *item) 44830744affSchristos { 44930744affSchristos item->flags &= ~CMDQ_WAITING; 45030744affSchristos } 45130744affSchristos 4524e179ddaSchristos /* Remove an item. */ 4534e179ddaSchristos static void 4544e179ddaSchristos cmdq_remove(struct cmdq_item *item) 4554e179ddaSchristos { 4564e179ddaSchristos if (item->client != NULL) 4574e179ddaSchristos server_client_unref(item->client); 45830744affSchristos if (item->cmdlist != NULL) 4594e179ddaSchristos cmd_list_free(item->cmdlist); 460e271dbb8Schristos cmdq_free_state(item->state); 4614e179ddaSchristos 462e271dbb8Schristos TAILQ_REMOVE(&item->queue->list, item, entry); 4634e179ddaSchristos 464e9a2d6faSchristos free(__UNCONST(item->name)); 4654e179ddaSchristos free(item); 4664e179ddaSchristos } 4674e179ddaSchristos 4684e179ddaSchristos /* Remove all subsequent items that match this item's group. */ 4694e179ddaSchristos static void 4704e179ddaSchristos cmdq_remove_group(struct cmdq_item *item) 4714e179ddaSchristos { 4724e179ddaSchristos struct cmdq_item *this, *next; 4734e179ddaSchristos 47430744affSchristos if (item->group == 0) 47530744affSchristos return; 4764e179ddaSchristos this = TAILQ_NEXT(item, entry); 4774e179ddaSchristos while (this != NULL) { 4784e179ddaSchristos next = TAILQ_NEXT(this, entry); 4794e179ddaSchristos if (this->group == item->group) 4804e179ddaSchristos cmdq_remove(this); 4814e179ddaSchristos this = next; 4824e179ddaSchristos } 4834e179ddaSchristos } 4844e179ddaSchristos 48546548964Swiz /* Empty command callback. */ 48646548964Swiz static enum cmd_retval 48746548964Swiz cmdq_empty_command(__unused struct cmdq_item *item, __unused void *data) 48846548964Swiz { 48946548964Swiz return (CMD_RETURN_NORMAL); 49046548964Swiz } 49146548964Swiz 4924e179ddaSchristos /* Get a command for the command queue. */ 4934e179ddaSchristos struct cmdq_item * 494e271dbb8Schristos cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state) 4954e179ddaSchristos { 4964e179ddaSchristos struct cmdq_item *item, *first = NULL, *last = NULL; 4974e179ddaSchristos struct cmd *cmd; 498e271dbb8Schristos const struct cmd_entry *entry; 499e271dbb8Schristos int created = 0; 500fe99a117Schristos 50146548964Swiz if ((cmd = cmd_list_first(cmdlist)) == NULL) 50246548964Swiz return (cmdq_get_callback(cmdq_empty_command, NULL)); 50346548964Swiz 504e271dbb8Schristos if (state == NULL) { 505e271dbb8Schristos state = cmdq_new_state(NULL, NULL, 0); 506e271dbb8Schristos created = 1; 50730744affSchristos } 5084e179ddaSchristos 509e271dbb8Schristos while (cmd != NULL) { 510e271dbb8Schristos entry = cmd_get_entry(cmd); 511e271dbb8Schristos 5124e179ddaSchristos item = xcalloc(1, sizeof *item); 513e271dbb8Schristos xasprintf(&item->name, "[%s/%p]", entry->name, item); 5144e179ddaSchristos item->type = CMDQ_COMMAND; 5154e179ddaSchristos 516e271dbb8Schristos item->group = cmd_get_group(cmd); 517e271dbb8Schristos item->state = cmdq_link_state(state); 5184e179ddaSchristos 5194e179ddaSchristos item->cmdlist = cmdlist; 5204e179ddaSchristos item->cmd = cmd; 5214e179ddaSchristos 5224e179ddaSchristos cmdlist->references++; 523e271dbb8Schristos log_debug("%s: %s group %u", __func__, item->name, item->group); 5244e179ddaSchristos 5254e179ddaSchristos if (first == NULL) 5264e179ddaSchristos first = item; 5274e179ddaSchristos if (last != NULL) 5284e179ddaSchristos last->next = item; 5294e179ddaSchristos last = item; 530e271dbb8Schristos 531e271dbb8Schristos cmd = cmd_list_next(cmd); 5324e179ddaSchristos } 533e271dbb8Schristos 534e271dbb8Schristos if (created) 535e271dbb8Schristos cmdq_free_state(state); 5364e179ddaSchristos return (first); 5374e179ddaSchristos } 5384e179ddaSchristos 539fe99a117Schristos /* Fill in flag for a command. */ 540fe99a117Schristos static enum cmd_retval 541fe99a117Schristos cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs, 542fe99a117Schristos const struct cmd_entry_flag *flag) 543fe99a117Schristos { 544fe99a117Schristos const char *value; 545fe99a117Schristos 546fe99a117Schristos if (flag->flag == 0) { 547e271dbb8Schristos cmd_find_from_client(fs, item->target_client, 0); 548fe99a117Schristos return (CMD_RETURN_NORMAL); 549fe99a117Schristos } 550fe99a117Schristos 551e271dbb8Schristos value = args_get(cmd_get_args(item->cmd), flag->flag); 552fe99a117Schristos if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) { 553fe99a117Schristos cmd_find_clear_state(fs, 0); 554fe99a117Schristos return (CMD_RETURN_ERROR); 555fe99a117Schristos } 556fe99a117Schristos return (CMD_RETURN_NORMAL); 557fe99a117Schristos } 558fe99a117Schristos 559e271dbb8Schristos /* Add message with command. */ 560e271dbb8Schristos static void 561e271dbb8Schristos cmdq_add_message(struct cmdq_item *item) 562e271dbb8Schristos { 563e271dbb8Schristos struct client *c = item->client; 564e271dbb8Schristos struct cmdq_state *state = item->state; 56546548964Swiz const char *key; 566e271dbb8Schristos char *tmp; 56746548964Swiz uid_t uid; 56846548964Swiz struct passwd *pw; 56946548964Swiz char *user = NULL; 570e271dbb8Schristos 571e271dbb8Schristos tmp = cmd_print(item->cmd); 572e271dbb8Schristos if (c != NULL) { 57346548964Swiz uid = proc_get_peer_uid(c->peer); 57446548964Swiz if (uid != (uid_t)-1 && uid != getuid()) { 57546548964Swiz if ((pw = getpwuid(uid)) != NULL) 57646548964Swiz xasprintf(&user, "[%s]", pw->pw_name); 57746548964Swiz else 57846548964Swiz user = xstrdup("[unknown]"); 57946548964Swiz } else 58046548964Swiz user = xstrdup(""); 581e271dbb8Schristos if (c->session != NULL && state->event.key != KEYC_NONE) { 582e271dbb8Schristos key = key_string_lookup_key(state->event.key, 0); 58346548964Swiz server_add_message("%s%s key %s: %s", c->name, user, 58446548964Swiz key, tmp); 58546548964Swiz } else { 58646548964Swiz server_add_message("%s%s command: %s", c->name, user, 58746548964Swiz tmp); 58846548964Swiz } 58946548964Swiz free(user); 590e271dbb8Schristos } else 591e271dbb8Schristos server_add_message("command: %s", tmp); 592e271dbb8Schristos free(tmp); 593e271dbb8Schristos } 594e271dbb8Schristos 5954e179ddaSchristos /* Fire command on command queue. */ 5964e179ddaSchristos static enum cmd_retval 5974e179ddaSchristos cmdq_fire_command(struct cmdq_item *item) 5984e179ddaSchristos { 599e271dbb8Schristos const char *name = cmdq_name(item->client); 600e271dbb8Schristos struct cmdq_state *state = item->state; 6014e179ddaSchristos struct cmd *cmd = item->cmd; 602e271dbb8Schristos struct args *args = cmd_get_args(cmd); 603e271dbb8Schristos const struct cmd_entry *entry = cmd_get_entry(cmd); 604e271dbb8Schristos struct client *tc, *saved = item->client; 6054e179ddaSchristos enum cmd_retval retval; 6064e179ddaSchristos struct cmd_find_state *fsp, fs; 607e271dbb8Schristos int flags, quiet = 0; 60830744affSchristos char *tmp; 6094e179ddaSchristos 610e271dbb8Schristos if (cfg_finished) 611e271dbb8Schristos cmdq_add_message(item); 61230744affSchristos if (log_get_level() > 1) { 61330744affSchristos tmp = cmd_print(cmd); 61430744affSchristos log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp); 61530744affSchristos free(tmp); 61630744affSchristos } 61730744affSchristos 618e271dbb8Schristos flags = !!(state->flags & CMDQ_STATE_CONTROL); 6194e179ddaSchristos cmdq_guard(item, "begin", flags); 6204e179ddaSchristos 6214e179ddaSchristos if (item->client == NULL) 622fe99a117Schristos item->client = cmd_find_client(item, NULL, 1); 623e271dbb8Schristos 624e271dbb8Schristos if (entry->flags & CMD_CLIENT_CANFAIL) 625e271dbb8Schristos quiet = 1; 626e271dbb8Schristos if (entry->flags & CMD_CLIENT_CFLAG) { 627e271dbb8Schristos tc = cmd_find_client(item, args_get(args, 'c'), quiet); 628e271dbb8Schristos if (tc == NULL && !quiet) { 629e271dbb8Schristos retval = CMD_RETURN_ERROR; 630e271dbb8Schristos goto out; 631e271dbb8Schristos } 632e271dbb8Schristos } else if (entry->flags & CMD_CLIENT_TFLAG) { 633e271dbb8Schristos tc = cmd_find_client(item, args_get(args, 't'), quiet); 634e271dbb8Schristos if (tc == NULL && !quiet) { 635e271dbb8Schristos retval = CMD_RETURN_ERROR; 636e271dbb8Schristos goto out; 637e271dbb8Schristos } 638e271dbb8Schristos } else 639e271dbb8Schristos tc = cmd_find_client(item, NULL, 1); 640e271dbb8Schristos item->target_client = tc; 641e271dbb8Schristos 642fe99a117Schristos retval = cmdq_find_flag(item, &item->source, &entry->source); 643fe99a117Schristos if (retval == CMD_RETURN_ERROR) 644fe99a117Schristos goto out; 645fe99a117Schristos retval = cmdq_find_flag(item, &item->target, &entry->target); 6464e179ddaSchristos if (retval == CMD_RETURN_ERROR) 6474e179ddaSchristos goto out; 6484e179ddaSchristos 649fe99a117Schristos retval = entry->exec(cmd, item); 650fe99a117Schristos if (retval == CMD_RETURN_ERROR) 6514e179ddaSchristos goto out; 652fe99a117Schristos 653fe99a117Schristos if (entry->flags & CMD_AFTERHOOK) { 654fe99a117Schristos if (cmd_find_valid_state(&item->target)) 655fe99a117Schristos fsp = &item->target; 656e271dbb8Schristos else if (cmd_find_valid_state(&item->state->current)) 657e271dbb8Schristos fsp = &item->state->current; 658fe99a117Schristos else if (cmd_find_from_client(&fs, item->client, 0) == 0) 6594e179ddaSchristos fsp = &fs; 660fe99a117Schristos else 661fe99a117Schristos goto out; 66230744affSchristos cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name); 6634e179ddaSchristos } 6644e179ddaSchristos 6654e179ddaSchristos out: 666e271dbb8Schristos item->client = saved; 667*890b6d91Swiz if (retval == CMD_RETURN_ERROR) { 668*890b6d91Swiz fsp = NULL; 669*890b6d91Swiz if (cmd_find_valid_state(&item->target)) 670*890b6d91Swiz fsp = &item->target; 671*890b6d91Swiz else if (cmd_find_valid_state(&item->state->current)) 672*890b6d91Swiz fsp = &item->state->current; 673*890b6d91Swiz else if (cmd_find_from_client(&fs, item->client, 0) == 0) 674*890b6d91Swiz fsp = &fs; 675*890b6d91Swiz cmdq_insert_hook(fsp != NULL ? fsp->s : NULL, item, fsp, 676*890b6d91Swiz "command-error"); 6774e179ddaSchristos cmdq_guard(item, "error", flags); 678*890b6d91Swiz } else 6794e179ddaSchristos cmdq_guard(item, "end", flags); 6804e179ddaSchristos return (retval); 6814e179ddaSchristos } 6824e179ddaSchristos 6834e179ddaSchristos /* Get a callback for the command queue. */ 6844e179ddaSchristos struct cmdq_item * 6854e179ddaSchristos cmdq_get_callback1(const char *name, cmdq_cb cb, void *data) 6864e179ddaSchristos { 6874e179ddaSchristos struct cmdq_item *item; 6884e179ddaSchristos 6894e179ddaSchristos item = xcalloc(1, sizeof *item); 69030744affSchristos xasprintf(&item->name, "[%s/%p]", name, item); 6914e179ddaSchristos item->type = CMDQ_CALLBACK; 6924e179ddaSchristos 6934e179ddaSchristos item->group = 0; 694e271dbb8Schristos item->state = cmdq_new_state(NULL, NULL, 0); 6954e179ddaSchristos 6964e179ddaSchristos item->cb = cb; 6974e179ddaSchristos item->data = data; 6984e179ddaSchristos 6994e179ddaSchristos return (item); 7004e179ddaSchristos } 7014e179ddaSchristos 70230744affSchristos /* Generic error callback. */ 70330744affSchristos static enum cmd_retval 70430744affSchristos cmdq_error_callback(struct cmdq_item *item, void *data) 70530744affSchristos { 70630744affSchristos char *error = data; 70730744affSchristos 70830744affSchristos cmdq_error(item, "%s", error); 70930744affSchristos free(error); 71030744affSchristos 71130744affSchristos return (CMD_RETURN_NORMAL); 71230744affSchristos } 71330744affSchristos 71430744affSchristos /* Get an error callback for the command queue. */ 71530744affSchristos struct cmdq_item * 71630744affSchristos cmdq_get_error(const char *error) 71730744affSchristos { 71830744affSchristos return (cmdq_get_callback(cmdq_error_callback, xstrdup(error))); 71930744affSchristos } 72030744affSchristos 7214e179ddaSchristos /* Fire callback on callback queue. */ 7224e179ddaSchristos static enum cmd_retval 7234e179ddaSchristos cmdq_fire_callback(struct cmdq_item *item) 7244e179ddaSchristos { 7254e179ddaSchristos return (item->cb(item, item->data)); 7264e179ddaSchristos } 7274e179ddaSchristos 7284e179ddaSchristos /* Process next item on command queue. */ 7294e179ddaSchristos u_int 7304e179ddaSchristos cmdq_next(struct client *c) 7314e179ddaSchristos { 7324e179ddaSchristos struct cmdq_list *queue = cmdq_get(c); 7334e179ddaSchristos const char *name = cmdq_name(c); 7344e179ddaSchristos struct cmdq_item *item; 7354e179ddaSchristos enum cmd_retval retval; 7364e179ddaSchristos u_int items = 0; 7374e179ddaSchristos static u_int number; 7384e179ddaSchristos 739e271dbb8Schristos if (TAILQ_EMPTY(&queue->list)) { 7404e179ddaSchristos log_debug("%s %s: empty", __func__, name); 7414e179ddaSchristos return (0); 7424e179ddaSchristos } 743e271dbb8Schristos if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) { 7444e179ddaSchristos log_debug("%s %s: waiting", __func__, name); 7455494e770Schristos return (0); 7465494e770Schristos } 747928fc495Schristos 7484e179ddaSchristos log_debug("%s %s: enter", __func__, name); 7494e179ddaSchristos for (;;) { 750e271dbb8Schristos item = queue->item = TAILQ_FIRST(&queue->list); 7514e179ddaSchristos if (item == NULL) 7524e179ddaSchristos break; 7534e179ddaSchristos log_debug("%s %s: %s (%d), flags %x", __func__, name, 7544e179ddaSchristos item->name, item->type, item->flags); 7554e179ddaSchristos 7564e179ddaSchristos /* 7574e179ddaSchristos * Any item with the waiting flag set waits until an external 7584e179ddaSchristos * event clears the flag (for example, a job - look at 7594e179ddaSchristos * run-shell). 7604e179ddaSchristos */ 7614e179ddaSchristos if (item->flags & CMDQ_WAITING) 7624e179ddaSchristos goto waiting; 7634e179ddaSchristos 7644e179ddaSchristos /* 7654e179ddaSchristos * Items are only fired once, once the fired flag is set, a 7664e179ddaSchristos * waiting flag can only be cleared by an external event. 7674e179ddaSchristos */ 7684e179ddaSchristos if (~item->flags & CMDQ_FIRED) { 7694e179ddaSchristos item->time = time(NULL); 7704e179ddaSchristos item->number = ++number; 7714e179ddaSchristos 772fe99a117Schristos switch (item->type) { 7734e179ddaSchristos case CMDQ_COMMAND: 7744e179ddaSchristos retval = cmdq_fire_command(item); 7754e179ddaSchristos 7764e179ddaSchristos /* 7774e179ddaSchristos * If a command returns an error, remove any 7784e179ddaSchristos * subsequent commands in the same group. 7794e179ddaSchristos */ 7804e179ddaSchristos if (retval == CMD_RETURN_ERROR) 7814e179ddaSchristos cmdq_remove_group(item); 7824e179ddaSchristos break; 7834e179ddaSchristos case CMDQ_CALLBACK: 7844e179ddaSchristos retval = cmdq_fire_callback(item); 7854e179ddaSchristos break; 7864e179ddaSchristos default: 7874e179ddaSchristos retval = CMD_RETURN_ERROR; 7884e179ddaSchristos break; 7894e179ddaSchristos } 7904e179ddaSchristos item->flags |= CMDQ_FIRED; 7914e179ddaSchristos 7924e179ddaSchristos if (retval == CMD_RETURN_WAIT) { 7934e179ddaSchristos item->flags |= CMDQ_WAITING; 7944e179ddaSchristos goto waiting; 7954e179ddaSchristos } 7964e179ddaSchristos items++; 7974e179ddaSchristos } 7984e179ddaSchristos cmdq_remove(item); 7994e179ddaSchristos } 800e271dbb8Schristos queue->item = NULL; 8014e179ddaSchristos 8024e179ddaSchristos log_debug("%s %s: exit (empty)", __func__, name); 8034e179ddaSchristos return (items); 8044e179ddaSchristos 8054e179ddaSchristos waiting: 8064e179ddaSchristos log_debug("%s %s: exit (wait)", __func__, name); 8074e179ddaSchristos return (items); 8084e179ddaSchristos } 8094e179ddaSchristos 810e271dbb8Schristos /* Get running item if any. */ 811e271dbb8Schristos struct cmdq_item * 812e271dbb8Schristos cmdq_running(struct client *c) 813e271dbb8Schristos { 814e271dbb8Schristos struct cmdq_list *queue = cmdq_get(c); 815e271dbb8Schristos 816e271dbb8Schristos if (queue->item == NULL) 817e271dbb8Schristos return (NULL); 818e271dbb8Schristos if (queue->item->flags & CMDQ_WAITING) 819e271dbb8Schristos return (NULL); 820e271dbb8Schristos return (queue->item); 821e271dbb8Schristos } 822e271dbb8Schristos 8234e179ddaSchristos /* Print a guard line. */ 8244e179ddaSchristos void 8254e179ddaSchristos cmdq_guard(struct cmdq_item *item, const char *guard, int flags) 8264e179ddaSchristos { 8274e179ddaSchristos struct client *c = item->client; 82868e6ba84Schristos long t = item->time; 82968e6ba84Schristos u_int number = item->number; 8304e179ddaSchristos 83168e6ba84Schristos if (c != NULL && (c->flags & CLIENT_CONTROL)) 832e271dbb8Schristos control_write(c, "%%%s %ld %u %d", guard, t, number, flags); 833928fc495Schristos } 834928fc495Schristos 835928fc495Schristos /* Show message from command. */ 8365494e770Schristos void 837f844e94eSwiz cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb) 838f844e94eSwiz { 839f844e94eSwiz server_client_print(item->client, parse, evb); 840f844e94eSwiz } 841f844e94eSwiz 842f844e94eSwiz /* Show message from command. */ 843f844e94eSwiz void 8444e179ddaSchristos cmdq_print(struct cmdq_item *item, const char *fmt, ...) 845928fc495Schristos { 846928fc495Schristos va_list ap; 847f844e94eSwiz struct evbuffer *evb; 848f844e94eSwiz 849f844e94eSwiz evb = evbuffer_new(); 850f844e94eSwiz if (evb == NULL) 851f844e94eSwiz fatalx("out of memory"); 852928fc495Schristos 853928fc495Schristos va_start(ap, fmt); 854f844e94eSwiz evbuffer_add_vprintf(evb, fmt, ap); 85568e6ba84Schristos va_end(ap); 85668e6ba84Schristos 857f844e94eSwiz cmdq_print_data(item, 0, evb); 858f844e94eSwiz evbuffer_free(evb); 859928fc495Schristos } 860928fc495Schristos 861928fc495Schristos /* Show error from command. */ 8625494e770Schristos void 8634e179ddaSchristos cmdq_error(struct cmdq_item *item, const char *fmt, ...) 864928fc495Schristos { 8654e179ddaSchristos struct client *c = item->client; 8664e179ddaSchristos struct cmd *cmd = item->cmd; 867928fc495Schristos va_list ap; 868e271dbb8Schristos char *msg, *tmp; 869e271dbb8Schristos const char *file; 870e271dbb8Schristos u_int line; 871928fc495Schristos 872928fc495Schristos va_start(ap, fmt); 87368e6ba84Schristos xvasprintf(&msg, fmt, ap); 874928fc495Schristos va_end(ap); 875928fc495Schristos 876fe99a117Schristos log_debug("%s: %s", __func__, msg); 877fe99a117Schristos 878e271dbb8Schristos if (c == NULL) { 879e271dbb8Schristos cmd_get_source(cmd, &file, &line); 880e271dbb8Schristos cfg_add_cause("%s:%u: %s", file, line, msg); 881e271dbb8Schristos } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { 882e271dbb8Schristos server_add_message("%s message: %s", c->name, msg); 883ed4e6cd4Schristos if (~c->flags & CLIENT_UTF8) { 884ed4e6cd4Schristos tmp = msg; 885ed4e6cd4Schristos msg = utf8_sanitize(tmp); 886ed4e6cd4Schristos free(tmp); 887ed4e6cd4Schristos } 88868e6ba84Schristos if (c->flags & CLIENT_CONTROL) 889e271dbb8Schristos control_write(c, "%s", msg); 89068e6ba84Schristos else 89168e6ba84Schristos file_error(c, "%s\n", msg); 892928fc495Schristos c->retval = 1; 893928fc495Schristos } else { 894928fc495Schristos *msg = toupper((u_char) *msg); 895e271dbb8Schristos status_message_set(c, -1, 1, 0, "%s", msg); 896928fc495Schristos } 897928fc495Schristos 898928fc495Schristos free(msg); 899928fc495Schristos } 900