Clarify text for new -A slightly, GitHub issue 3508.
[tmux-openbsd.git] / cmd-queue.c
blobce6cab380673f8482fdf30543cbcb8af92c652e3
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
21 #include <ctype.h>
22 #include <pwd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <unistd.h>
27 #include <vis.h>
29 #include "tmux.h"
31 /* Command queue flags. */
32 #define CMDQ_FIRED 0x1
33 #define CMDQ_WAITING 0x2
35 /* Command queue item type. */
36 enum cmdq_type {
37 CMDQ_COMMAND,
38 CMDQ_CALLBACK,
41 /* Command queue item. */
42 struct cmdq_item {
43 char *name;
44 struct cmdq_list *queue;
45 struct cmdq_item *next;
47 struct client *client;
48 struct client *target_client;
50 enum cmdq_type type;
51 u_int group;
53 u_int number;
54 time_t time;
56 int flags;
58 struct cmdq_state *state;
59 struct cmd_find_state source;
60 struct cmd_find_state target;
62 struct cmd_list *cmdlist;
63 struct cmd *cmd;
65 cmdq_cb cb;
66 void *data;
68 TAILQ_ENTRY(cmdq_item) entry;
70 TAILQ_HEAD(cmdq_item_list, cmdq_item);
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.
79 struct cmdq_state {
80 int references;
81 int flags;
83 struct format_tree *formats;
85 struct key_event event;
86 struct cmd_find_state current;
89 /* Command queue. */
90 struct cmdq_list {
91 struct cmdq_item *item;
92 struct cmdq_item_list list;
95 /* Get command queue name. */
96 static const char *
97 cmdq_name(struct client *c)
99 static char s[256];
101 if (c == NULL)
102 return ("<global>");
103 if (c->name != NULL)
104 xsnprintf(s, sizeof s, "<%s>", c->name);
105 else
106 xsnprintf(s, sizeof s, "<%p>", c);
107 return (s);
110 /* Get command queue from client. */
111 static struct cmdq_list *
112 cmdq_get(struct client *c)
114 static struct cmdq_list *global_queue;
116 if (c == NULL) {
117 if (global_queue == NULL)
118 global_queue = cmdq_new();
119 return (global_queue);
121 return (c->queue);
124 /* Create a queue. */
125 struct cmdq_list *
126 cmdq_new(void)
128 struct cmdq_list *queue;
130 queue = xcalloc(1, sizeof *queue);
131 TAILQ_INIT (&queue->list);
132 return (queue);
135 /* Free a queue. */
136 void
137 cmdq_free(struct cmdq_list *queue)
139 if (!TAILQ_EMPTY(&queue->list))
140 fatalx("queue not empty");
141 free(queue);
144 /* Get item name. */
145 const char *
146 cmdq_get_name(struct cmdq_item *item)
148 return (item->name);
151 /* Get item client. */
152 struct client *
153 cmdq_get_client(struct cmdq_item *item)
155 return (item->client);
158 /* Get item target client. */
159 struct client *
160 cmdq_get_target_client(struct cmdq_item *item)
162 return (item->target_client);
165 /* Get item state. */
166 struct cmdq_state *
167 cmdq_get_state(struct cmdq_item *item)
169 return (item->state);
172 /* Get item target. */
173 struct cmd_find_state *
174 cmdq_get_target(struct cmdq_item *item)
176 return (&item->target);
179 /* Get item source. */
180 struct cmd_find_state *
181 cmdq_get_source(struct cmdq_item *item)
183 return (&item->source);
186 /* Get state event. */
187 struct key_event *
188 cmdq_get_event(struct cmdq_item *item)
190 return (&item->state->event);
193 /* Get state current target. */
194 struct cmd_find_state *
195 cmdq_get_current(struct cmdq_item *item)
197 return (&item->state->current);
200 /* Get state flags. */
202 cmdq_get_flags(struct cmdq_item *item)
204 return (item->state->flags);
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)
212 struct cmdq_state *state;
214 state = xcalloc(1, sizeof *state);
215 state->references = 1;
216 state->flags = flags;
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);
227 return (state);
230 /* Add a reference to a state. */
231 struct cmdq_state *
232 cmdq_link_state(struct cmdq_state *state)
234 state->references++;
235 return (state);
238 /* Make a copy of a state. */
239 struct cmdq_state *
240 cmdq_copy_state(struct cmdq_state *state)
242 return (cmdq_new_state(&state->current, &state->event, state->flags));
245 /* Free a state. */
246 void
247 cmdq_free_state(struct cmdq_state *state)
249 if (--state->references != 0)
250 return;
252 if (state->formats != NULL)
253 format_free(state->formats);
254 free(state);
257 /* Add a format to command queue. */
258 void
259 cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...)
261 va_list ap;
262 char *value;
264 va_start(ap, fmt);
265 xvasprintf(&value, fmt, ap);
266 va_end(ap);
268 if (state->formats == NULL)
269 state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
270 format_add(state->formats, key, "%s", value);
272 free(value);
275 /* Add formats to command queue. */
276 void
277 cmdq_add_formats(struct cmdq_state *state, struct format_tree *ft)
279 if (state->formats == NULL)
280 state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
281 format_merge(state->formats, ft);
284 /* Merge formats from item. */
285 void
286 cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
288 const struct cmd_entry *entry;
290 if (item->cmd != NULL) {
291 entry = cmd_get_entry(item->cmd);
292 format_add(ft, "command", "%s", entry->name);
294 if (item->state->formats != NULL)
295 format_merge(ft, item->state->formats);
298 /* Append an item. */
299 struct cmdq_item *
300 cmdq_append(struct client *c, struct cmdq_item *item)
302 struct cmdq_list *queue = cmdq_get(c);
303 struct cmdq_item *next;
305 do {
306 next = item->next;
307 item->next = NULL;
309 if (c != NULL)
310 c->references++;
311 item->client = c;
313 item->queue = queue;
314 TAILQ_INSERT_TAIL(&queue->list, item, entry);
315 log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
317 item = next;
318 } while (item != NULL);
319 return (TAILQ_LAST(&queue->list, cmdq_item_list));
322 /* Insert an item. */
323 struct cmdq_item *
324 cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
326 struct client *c = after->client;
327 struct cmdq_list *queue = after->queue;
328 struct cmdq_item *next;
330 do {
331 next = item->next;
332 item->next = after->next;
333 after->next = item;
335 if (c != NULL)
336 c->references++;
337 item->client = c;
339 item->queue = queue;
340 TAILQ_INSERT_AFTER(&queue->list, after, item, entry);
341 log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
342 item->name, after->name);
344 after = item;
345 item = next;
346 } while (item != NULL);
347 return (after);
350 /* Insert a hook. */
351 void
352 cmdq_insert_hook(struct session *s, struct cmdq_item *item,
353 struct cmd_find_state *current, const char *fmt, ...)
355 struct cmdq_state *state = item->state;
356 struct cmd *cmd = item->cmd;
357 struct args *args = cmd_get_args(cmd);
358 struct args_entry *ae;
359 struct args_value *av;
360 struct options *oo;
361 va_list ap;
362 char *name, tmp[32], flag, *arguments;
363 u_int i;
364 const char *value;
365 struct cmdq_item *new_item;
366 struct cmdq_state *new_state;
367 struct options_entry *o;
368 struct options_array_item *a;
369 struct cmd_list *cmdlist;
371 if (item->state->flags & CMDQ_STATE_NOHOOKS)
372 return;
373 if (s == NULL)
374 oo = global_s_options;
375 else
376 oo = s->options;
378 va_start(ap, fmt);
379 xvasprintf(&name, fmt, ap);
380 va_end(ap);
382 o = options_get(oo, name);
383 if (o == NULL) {
384 free(name);
385 return;
387 log_debug("running hook %s (parent %p)", name, item);
390 * The hooks get a new state because they should not update the current
391 * target or formats for any subsequent commands.
393 new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
394 cmdq_add_format(new_state, "hook", "%s", name);
396 arguments = args_print(args);
397 cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
398 free(arguments);
400 for (i = 0; i < args_count(args); i++) {
401 xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
402 cmdq_add_format(new_state, tmp, "%s", args_string(args, i));
404 flag = args_first(args, &ae);
405 while (flag != 0) {
406 value = args_get(args, flag);
407 if (value == NULL) {
408 xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
409 cmdq_add_format(new_state, tmp, "1");
410 } else {
411 xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
412 cmdq_add_format(new_state, tmp, "%s", value);
415 i = 0;
416 av = args_first_value(args, flag);
417 while (av != NULL) {
418 xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
419 cmdq_add_format(new_state, tmp, "%s", av->string);
420 i++;
421 av = args_next_value(av);
424 flag = args_next(&ae);
427 a = options_array_first(o);
428 while (a != NULL) {
429 cmdlist = options_array_item_value(a)->cmdlist;
430 if (cmdlist != NULL) {
431 new_item = cmdq_get_command(cmdlist, new_state);
432 if (item != NULL)
433 item = cmdq_insert_after(item, new_item);
434 else
435 item = cmdq_append(NULL, new_item);
437 a = options_array_next(a);
440 cmdq_free_state(new_state);
441 free(name);
444 /* Continue processing command queue. */
445 void
446 cmdq_continue(struct cmdq_item *item)
448 item->flags &= ~CMDQ_WAITING;
451 /* Remove an item. */
452 static void
453 cmdq_remove(struct cmdq_item *item)
455 if (item->client != NULL)
456 server_client_unref(item->client);
457 if (item->cmdlist != NULL)
458 cmd_list_free(item->cmdlist);
459 cmdq_free_state(item->state);
461 TAILQ_REMOVE(&item->queue->list, item, entry);
463 free(item->name);
464 free(item);
467 /* Remove all subsequent items that match this item's group. */
468 static void
469 cmdq_remove_group(struct cmdq_item *item)
471 struct cmdq_item *this, *next;
473 if (item->group == 0)
474 return;
475 this = TAILQ_NEXT(item, entry);
476 while (this != NULL) {
477 next = TAILQ_NEXT(this, entry);
478 if (this->group == item->group)
479 cmdq_remove(this);
480 this = next;
484 /* Empty command callback. */
485 static enum cmd_retval
486 cmdq_empty_command(__unused struct cmdq_item *item, __unused void *data)
488 return (CMD_RETURN_NORMAL);
491 /* Get a command for the command queue. */
492 struct cmdq_item *
493 cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
495 struct cmdq_item *item, *first = NULL, *last = NULL;
496 struct cmd *cmd;
497 const struct cmd_entry *entry;
498 int created = 0;
500 if ((cmd = cmd_list_first(cmdlist)) == NULL)
501 return (cmdq_get_callback(cmdq_empty_command, NULL));
503 if (state == NULL) {
504 state = cmdq_new_state(NULL, NULL, 0);
505 created = 1;
508 while (cmd != NULL) {
509 entry = cmd_get_entry(cmd);
511 item = xcalloc(1, sizeof *item);
512 xasprintf(&item->name, "[%s/%p]", entry->name, item);
513 item->type = CMDQ_COMMAND;
515 item->group = cmd_get_group(cmd);
516 item->state = cmdq_link_state(state);
518 item->cmdlist = cmdlist;
519 item->cmd = cmd;
521 cmdlist->references++;
522 log_debug("%s: %s group %u", __func__, item->name, item->group);
524 if (first == NULL)
525 first = item;
526 if (last != NULL)
527 last->next = item;
528 last = item;
530 cmd = cmd_list_next(cmd);
533 if (created)
534 cmdq_free_state(state);
535 return (first);
538 /* Fill in flag for a command. */
539 static enum cmd_retval
540 cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
541 const struct cmd_entry_flag *flag)
543 const char *value;
545 if (flag->flag == 0) {
546 cmd_find_from_client(fs, item->target_client, 0);
547 return (CMD_RETURN_NORMAL);
550 value = args_get(cmd_get_args(item->cmd), flag->flag);
551 if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
552 cmd_find_clear_state(fs, 0);
553 return (CMD_RETURN_ERROR);
555 return (CMD_RETURN_NORMAL);
558 /* Add message with command. */
559 static void
560 cmdq_add_message(struct cmdq_item *item)
562 struct client *c = item->client;
563 struct cmdq_state *state = item->state;
564 const char *key;
565 char *tmp;
566 uid_t uid;
567 struct passwd *pw;
568 char *user = NULL;
570 tmp = cmd_print(item->cmd);
571 if (c != NULL) {
572 uid = proc_get_peer_uid(c->peer);
573 if (uid != (uid_t)-1 && uid != getuid()) {
574 if ((pw = getpwuid(uid)) != NULL)
575 xasprintf(&user, "[%s]", pw->pw_name);
576 else
577 user = xstrdup("[unknown]");
578 } else
579 user = xstrdup("");
580 if (c->session != NULL && state->event.key != KEYC_NONE) {
581 key = key_string_lookup_key(state->event.key, 0);
582 server_add_message("%s%s key %s: %s", c->name, user,
583 key, tmp);
584 } else {
585 server_add_message("%s%s command: %s", c->name, user,
586 tmp);
588 free(user);
589 } else
590 server_add_message("command: %s", tmp);
591 free(tmp);
594 /* Fire command on command queue. */
595 static enum cmd_retval
596 cmdq_fire_command(struct cmdq_item *item)
598 const char *name = cmdq_name(item->client);
599 struct cmdq_state *state = item->state;
600 struct cmd *cmd = item->cmd;
601 struct args *args = cmd_get_args(cmd);
602 const struct cmd_entry *entry = cmd_get_entry(cmd);
603 struct client *tc, *saved = item->client;
604 enum cmd_retval retval;
605 struct cmd_find_state *fsp, fs;
606 int flags, quiet = 0;
607 char *tmp;
609 if (cfg_finished)
610 cmdq_add_message(item);
611 if (log_get_level() > 1) {
612 tmp = cmd_print(cmd);
613 log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
614 free(tmp);
617 flags = !!(state->flags & CMDQ_STATE_CONTROL);
618 cmdq_guard(item, "begin", flags);
620 if (item->client == NULL)
621 item->client = cmd_find_client(item, NULL, 1);
623 if (entry->flags & CMD_CLIENT_CANFAIL)
624 quiet = 1;
625 if (entry->flags & CMD_CLIENT_CFLAG) {
626 tc = cmd_find_client(item, args_get(args, 'c'), quiet);
627 if (tc == NULL && !quiet) {
628 retval = CMD_RETURN_ERROR;
629 goto out;
631 } else if (entry->flags & CMD_CLIENT_TFLAG) {
632 tc = cmd_find_client(item, args_get(args, 't'), quiet);
633 if (tc == NULL && !quiet) {
634 retval = CMD_RETURN_ERROR;
635 goto out;
637 } else
638 tc = cmd_find_client(item, NULL, 1);
639 item->target_client = tc;
641 retval = cmdq_find_flag(item, &item->source, &entry->source);
642 if (retval == CMD_RETURN_ERROR)
643 goto out;
644 retval = cmdq_find_flag(item, &item->target, &entry->target);
645 if (retval == CMD_RETURN_ERROR)
646 goto out;
648 retval = entry->exec(cmd, item);
649 if (retval == CMD_RETURN_ERROR)
650 goto out;
652 if (entry->flags & CMD_AFTERHOOK) {
653 if (cmd_find_valid_state(&item->target))
654 fsp = &item->target;
655 else if (cmd_find_valid_state(&item->state->current))
656 fsp = &item->state->current;
657 else if (cmd_find_from_client(&fs, item->client, 0) == 0)
658 fsp = &fs;
659 else
660 goto out;
661 cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
664 out:
665 item->client = saved;
666 if (retval == CMD_RETURN_ERROR)
667 cmdq_guard(item, "error", flags);
668 else
669 cmdq_guard(item, "end", flags);
670 return (retval);
673 /* Get a callback for the command queue. */
674 struct cmdq_item *
675 cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
677 struct cmdq_item *item;
679 item = xcalloc(1, sizeof *item);
680 xasprintf(&item->name, "[%s/%p]", name, item);
681 item->type = CMDQ_CALLBACK;
683 item->group = 0;
684 item->state = cmdq_new_state(NULL, NULL, 0);
686 item->cb = cb;
687 item->data = data;
689 return (item);
692 /* Generic error callback. */
693 static enum cmd_retval
694 cmdq_error_callback(struct cmdq_item *item, void *data)
696 char *error = data;
698 cmdq_error(item, "%s", error);
699 free(error);
701 return (CMD_RETURN_NORMAL);
704 /* Get an error callback for the command queue. */
705 struct cmdq_item *
706 cmdq_get_error(const char *error)
708 return (cmdq_get_callback(cmdq_error_callback, xstrdup(error)));
711 /* Fire callback on callback queue. */
712 static enum cmd_retval
713 cmdq_fire_callback(struct cmdq_item *item)
715 return (item->cb(item, item->data));
718 /* Process next item on command queue. */
719 u_int
720 cmdq_next(struct client *c)
722 struct cmdq_list *queue = cmdq_get(c);
723 const char *name = cmdq_name(c);
724 struct cmdq_item *item;
725 enum cmd_retval retval;
726 u_int items = 0;
727 static u_int number;
729 if (TAILQ_EMPTY(&queue->list)) {
730 log_debug("%s %s: empty", __func__, name);
731 return (0);
733 if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) {
734 log_debug("%s %s: waiting", __func__, name);
735 return (0);
738 log_debug("%s %s: enter", __func__, name);
739 for (;;) {
740 item = queue->item = TAILQ_FIRST(&queue->list);
741 if (item == NULL)
742 break;
743 log_debug("%s %s: %s (%d), flags %x", __func__, name,
744 item->name, item->type, item->flags);
747 * Any item with the waiting flag set waits until an external
748 * event clears the flag (for example, a job - look at
749 * run-shell).
751 if (item->flags & CMDQ_WAITING)
752 goto waiting;
755 * Items are only fired once, once the fired flag is set, a
756 * waiting flag can only be cleared by an external event.
758 if (~item->flags & CMDQ_FIRED) {
759 item->time = time(NULL);
760 item->number = ++number;
762 switch (item->type) {
763 case CMDQ_COMMAND:
764 retval = cmdq_fire_command(item);
767 * If a command returns an error, remove any
768 * subsequent commands in the same group.
770 if (retval == CMD_RETURN_ERROR)
771 cmdq_remove_group(item);
772 break;
773 case CMDQ_CALLBACK:
774 retval = cmdq_fire_callback(item);
775 break;
776 default:
777 retval = CMD_RETURN_ERROR;
778 break;
780 item->flags |= CMDQ_FIRED;
782 if (retval == CMD_RETURN_WAIT) {
783 item->flags |= CMDQ_WAITING;
784 goto waiting;
786 items++;
788 cmdq_remove(item);
790 queue->item = NULL;
792 log_debug("%s %s: exit (empty)", __func__, name);
793 return (items);
795 waiting:
796 log_debug("%s %s: exit (wait)", __func__, name);
797 return (items);
800 /* Get running item if any. */
801 struct cmdq_item *
802 cmdq_running(struct client *c)
804 struct cmdq_list *queue = cmdq_get(c);
806 if (queue->item == NULL)
807 return (NULL);
808 if (queue->item->flags & CMDQ_WAITING)
809 return (NULL);
810 return (queue->item);
813 /* Print a guard line. */
814 void
815 cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
817 struct client *c = item->client;
818 long t = item->time;
819 u_int number = item->number;
821 if (c != NULL && (c->flags & CLIENT_CONTROL))
822 control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
825 /* Show message from command. */
826 void
827 cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb)
829 server_client_print(item->client, parse, evb);
832 /* Show message from command. */
833 void
834 cmdq_print(struct cmdq_item *item, const char *fmt, ...)
836 va_list ap;
837 struct evbuffer *evb;
839 evb = evbuffer_new();
840 if (evb == NULL)
841 fatalx("out of memory");
843 va_start(ap, fmt);
844 evbuffer_add_vprintf(evb, fmt, ap);
845 va_end(ap);
847 cmdq_print_data(item, 0, evb);
848 evbuffer_free(evb);
851 /* Show error from command. */
852 void
853 cmdq_error(struct cmdq_item *item, const char *fmt, ...)
855 struct client *c = item->client;
856 struct cmd *cmd = item->cmd;
857 va_list ap;
858 char *msg, *tmp;
859 const char *file;
860 u_int line;
862 va_start(ap, fmt);
863 xvasprintf(&msg, fmt, ap);
864 va_end(ap);
866 log_debug("%s: %s", __func__, msg);
868 if (c == NULL) {
869 cmd_get_source(cmd, &file, &line);
870 cfg_add_cause("%s:%u: %s", file, line, msg);
871 } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
872 server_add_message("%s message: %s", c->name, msg);
873 if (~c->flags & CLIENT_UTF8) {
874 tmp = msg;
875 msg = utf8_sanitize(tmp);
876 free(tmp);
878 if (c->flags & CLIENT_CONTROL)
879 control_write(c, "%s", msg);
880 else
881 file_error(c, "%s\n", msg);
882 c->retval = 1;
883 } else {
884 *msg = toupper((u_char) *msg);
885 status_message_set(c, -1, 1, 0, "%s", msg);
888 free(msg);