Merge branch 'obsd-master'
[tmux.git] / cmd-queue.c
blobe5188c5429839cd9a2c96b2beac6ce324717f532
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>
28 #include "tmux.h"
30 /* Command queue flags. */
31 #define CMDQ_FIRED 0x1
32 #define CMDQ_WAITING 0x2
34 /* Command queue item type. */
35 enum cmdq_type {
36 CMDQ_COMMAND,
37 CMDQ_CALLBACK,
40 /* Command queue item. */
41 struct cmdq_item {
42 char *name;
43 struct cmdq_list *queue;
44 struct cmdq_item *next;
46 struct client *client;
47 struct client *target_client;
49 enum cmdq_type type;
50 u_int group;
52 u_int number;
53 time_t time;
55 int flags;
57 struct cmdq_state *state;
58 struct cmd_find_state source;
59 struct cmd_find_state target;
61 struct cmd_list *cmdlist;
62 struct cmd *cmd;
64 cmdq_cb cb;
65 void *data;
67 TAILQ_ENTRY(cmdq_item) entry;
69 TAILQ_HEAD(cmdq_item_list, cmdq_item);
72 * Command queue state. This is the context for commands on the command queue.
73 * It holds information about how the commands were fired (the key and flags),
74 * any additional formats for the commands, and the current default target.
75 * Multiple commands can share the same state and a command may update the
76 * default target.
78 struct cmdq_state {
79 int references;
80 int flags;
82 struct format_tree *formats;
84 struct key_event event;
85 struct cmd_find_state current;
88 /* Command queue. */
89 struct cmdq_list {
90 struct cmdq_item *item;
91 struct cmdq_item_list list;
94 /* Get command queue name. */
95 static const char *
96 cmdq_name(struct client *c)
98 static char s[256];
100 if (c == NULL)
101 return ("<global>");
102 if (c->name != NULL)
103 xsnprintf(s, sizeof s, "<%s>", c->name);
104 else
105 xsnprintf(s, sizeof s, "<%p>", c);
106 return (s);
109 /* Get command queue from client. */
110 static struct cmdq_list *
111 cmdq_get(struct client *c)
113 static struct cmdq_list *global_queue;
115 if (c == NULL) {
116 if (global_queue == NULL)
117 global_queue = cmdq_new();
118 return (global_queue);
120 return (c->queue);
123 /* Create a queue. */
124 struct cmdq_list *
125 cmdq_new(void)
127 struct cmdq_list *queue;
129 queue = xcalloc(1, sizeof *queue);
130 TAILQ_INIT (&queue->list);
131 return (queue);
134 /* Free a queue. */
135 void
136 cmdq_free(struct cmdq_list *queue)
138 if (!TAILQ_EMPTY(&queue->list))
139 fatalx("queue not empty");
140 free(queue);
143 /* Get item name. */
144 const char *
145 cmdq_get_name(struct cmdq_item *item)
147 return (item->name);
150 /* Get item client. */
151 struct client *
152 cmdq_get_client(struct cmdq_item *item)
154 return (item->client);
157 /* Get item target client. */
158 struct client *
159 cmdq_get_target_client(struct cmdq_item *item)
161 return (item->target_client);
164 /* Get item state. */
165 struct cmdq_state *
166 cmdq_get_state(struct cmdq_item *item)
168 return (item->state);
171 /* Get item target. */
172 struct cmd_find_state *
173 cmdq_get_target(struct cmdq_item *item)
175 return (&item->target);
178 /* Get item source. */
179 struct cmd_find_state *
180 cmdq_get_source(struct cmdq_item *item)
182 return (&item->source);
185 /* Get state event. */
186 struct key_event *
187 cmdq_get_event(struct cmdq_item *item)
189 return (&item->state->event);
192 /* Get state current target. */
193 struct cmd_find_state *
194 cmdq_get_current(struct cmdq_item *item)
196 return (&item->state->current);
199 /* Get state flags. */
201 cmdq_get_flags(struct cmdq_item *item)
203 return (item->state->flags);
206 /* Create a new state. */
207 struct cmdq_state *
208 cmdq_new_state(struct cmd_find_state *current, struct key_event *event,
209 int flags)
211 struct cmdq_state *state;
213 state = xcalloc(1, sizeof *state);
214 state->references = 1;
215 state->flags = flags;
217 if (event != NULL)
218 memcpy(&state->event, event, sizeof state->event);
219 else
220 state->event.key = KEYC_NONE;
221 if (current != NULL && cmd_find_valid_state(current))
222 cmd_find_copy_state(&state->current, current);
223 else
224 cmd_find_clear_state(&state->current, 0);
226 return (state);
229 /* Add a reference to a state. */
230 struct cmdq_state *
231 cmdq_link_state(struct cmdq_state *state)
233 state->references++;
234 return (state);
237 /* Make a copy of a state. */
238 struct cmdq_state *
239 cmdq_copy_state(struct cmdq_state *state)
241 return (cmdq_new_state(&state->current, &state->event, state->flags));
244 /* Free a state. */
245 void
246 cmdq_free_state(struct cmdq_state *state)
248 if (--state->references != 0)
249 return;
251 if (state->formats != NULL)
252 format_free(state->formats);
253 free(state);
256 /* Add a format to command queue. */
257 void
258 cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...)
260 va_list ap;
261 char *value;
263 va_start(ap, fmt);
264 xvasprintf(&value, fmt, ap);
265 va_end(ap);
267 if (state->formats == NULL)
268 state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
269 format_add(state->formats, key, "%s", value);
271 free(value);
274 /* Add formats to command queue. */
275 void
276 cmdq_add_formats(struct cmdq_state *state, struct format_tree *ft)
278 if (state->formats == NULL)
279 state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
280 format_merge(state->formats, ft);
283 /* Merge formats from item. */
284 void
285 cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
287 const struct cmd_entry *entry;
289 if (item->cmd != NULL) {
290 entry = cmd_get_entry(item->cmd);
291 format_add(ft, "command", "%s", entry->name);
293 if (item->state->formats != NULL)
294 format_merge(ft, item->state->formats);
297 /* Append an item. */
298 struct cmdq_item *
299 cmdq_append(struct client *c, struct cmdq_item *item)
301 struct cmdq_list *queue = cmdq_get(c);
302 struct cmdq_item *next;
304 do {
305 next = item->next;
306 item->next = NULL;
308 if (c != NULL)
309 c->references++;
310 item->client = c;
312 item->queue = queue;
313 TAILQ_INSERT_TAIL(&queue->list, item, entry);
314 log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
316 item = next;
317 } while (item != NULL);
318 return (TAILQ_LAST(&queue->list, cmdq_item_list));
321 /* Insert an item. */
322 struct cmdq_item *
323 cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
325 struct client *c = after->client;
326 struct cmdq_list *queue = after->queue;
327 struct cmdq_item *next;
329 do {
330 next = item->next;
331 item->next = after->next;
332 after->next = item;
334 if (c != NULL)
335 c->references++;
336 item->client = c;
338 item->queue = queue;
339 TAILQ_INSERT_AFTER(&queue->list, after, item, entry);
340 log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
341 item->name, after->name);
343 after = item;
344 item = next;
345 } while (item != NULL);
346 return (after);
349 /* Insert a hook. */
350 void
351 cmdq_insert_hook(struct session *s, struct cmdq_item *item,
352 struct cmd_find_state *current, const char *fmt, ...)
354 struct cmdq_state *state = item->state;
355 struct cmd *cmd = item->cmd;
356 struct args *args = cmd_get_args(cmd);
357 struct args_entry *ae;
358 struct args_value *av;
359 struct options *oo;
360 va_list ap;
361 char *name, tmp[32], flag, *arguments;
362 u_int i;
363 const char *value;
364 struct cmdq_item *new_item;
365 struct cmdq_state *new_state;
366 struct options_entry *o;
367 struct options_array_item *a;
368 struct cmd_list *cmdlist;
370 if (item->state->flags & CMDQ_STATE_NOHOOKS)
371 return;
372 if (s == NULL)
373 oo = global_s_options;
374 else
375 oo = s->options;
377 va_start(ap, fmt);
378 xvasprintf(&name, fmt, ap);
379 va_end(ap);
381 o = options_get(oo, name);
382 if (o == NULL) {
383 free(name);
384 return;
386 log_debug("running hook %s (parent %p)", name, item);
389 * The hooks get a new state because they should not update the current
390 * target or formats for any subsequent commands.
392 new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
393 cmdq_add_format(new_state, "hook", "%s", name);
395 arguments = args_print(args);
396 cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
397 free(arguments);
399 for (i = 0; i < args_count(args); i++) {
400 xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
401 cmdq_add_format(new_state, tmp, "%s", args_string(args, i));
403 flag = args_first(args, &ae);
404 while (flag != 0) {
405 value = args_get(args, flag);
406 if (value == NULL) {
407 xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
408 cmdq_add_format(new_state, tmp, "1");
409 } else {
410 xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
411 cmdq_add_format(new_state, tmp, "%s", value);
414 i = 0;
415 av = args_first_value(args, flag);
416 while (av != NULL) {
417 xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
418 cmdq_add_format(new_state, tmp, "%s", av->string);
419 i++;
420 av = args_next_value(av);
423 flag = args_next(&ae);
426 a = options_array_first(o);
427 while (a != NULL) {
428 cmdlist = options_array_item_value(a)->cmdlist;
429 if (cmdlist != NULL) {
430 new_item = cmdq_get_command(cmdlist, new_state);
431 if (item != NULL)
432 item = cmdq_insert_after(item, new_item);
433 else
434 item = cmdq_append(NULL, new_item);
436 a = options_array_next(a);
439 cmdq_free_state(new_state);
440 free(name);
443 /* Continue processing command queue. */
444 void
445 cmdq_continue(struct cmdq_item *item)
447 item->flags &= ~CMDQ_WAITING;
450 /* Remove an item. */
451 static void
452 cmdq_remove(struct cmdq_item *item)
454 if (item->client != NULL)
455 server_client_unref(item->client);
456 if (item->cmdlist != NULL)
457 cmd_list_free(item->cmdlist);
458 cmdq_free_state(item->state);
460 TAILQ_REMOVE(&item->queue->list, item, entry);
462 free(item->name);
463 free(item);
466 /* Remove all subsequent items that match this item's group. */
467 static void
468 cmdq_remove_group(struct cmdq_item *item)
470 struct cmdq_item *this, *next;
472 if (item->group == 0)
473 return;
474 this = TAILQ_NEXT(item, entry);
475 while (this != NULL) {
476 next = TAILQ_NEXT(this, entry);
477 if (this->group == item->group)
478 cmdq_remove(this);
479 this = next;
483 /* Empty command callback. */
484 static enum cmd_retval
485 cmdq_empty_command(__unused struct cmdq_item *item, __unused void *data)
487 return (CMD_RETURN_NORMAL);
490 /* Get a command for the command queue. */
491 struct cmdq_item *
492 cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
494 struct cmdq_item *item, *first = NULL, *last = NULL;
495 struct cmd *cmd;
496 const struct cmd_entry *entry;
497 int created = 0;
499 if ((cmd = cmd_list_first(cmdlist)) == NULL)
500 return (cmdq_get_callback(cmdq_empty_command, NULL));
502 if (state == NULL) {
503 state = cmdq_new_state(NULL, NULL, 0);
504 created = 1;
507 while (cmd != NULL) {
508 entry = cmd_get_entry(cmd);
510 item = xcalloc(1, sizeof *item);
511 xasprintf(&item->name, "[%s/%p]", entry->name, item);
512 item->type = CMDQ_COMMAND;
514 item->group = cmd_get_group(cmd);
515 item->state = cmdq_link_state(state);
517 item->cmdlist = cmdlist;
518 item->cmd = cmd;
520 cmdlist->references++;
521 log_debug("%s: %s group %u", __func__, item->name, item->group);
523 if (first == NULL)
524 first = item;
525 if (last != NULL)
526 last->next = item;
527 last = item;
529 cmd = cmd_list_next(cmd);
532 if (created)
533 cmdq_free_state(state);
534 return (first);
537 /* Fill in flag for a command. */
538 static enum cmd_retval
539 cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
540 const struct cmd_entry_flag *flag)
542 const char *value;
544 if (flag->flag == 0) {
545 cmd_find_from_client(fs, item->target_client, 0);
546 return (CMD_RETURN_NORMAL);
549 value = args_get(cmd_get_args(item->cmd), flag->flag);
550 if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
551 cmd_find_clear_state(fs, 0);
552 return (CMD_RETURN_ERROR);
554 return (CMD_RETURN_NORMAL);
557 /* Add message with command. */
558 static void
559 cmdq_add_message(struct cmdq_item *item)
561 struct client *c = item->client;
562 struct cmdq_state *state = item->state;
563 const char *key;
564 char *tmp;
565 uid_t uid;
566 struct passwd *pw;
567 char *user = NULL;
569 tmp = cmd_print(item->cmd);
570 if (c != NULL) {
571 uid = proc_get_peer_uid(c->peer);
572 if (uid != (uid_t)-1 && uid != getuid()) {
573 if ((pw = getpwuid(uid)) != NULL)
574 xasprintf(&user, "[%s]", pw->pw_name);
575 else
576 user = xstrdup("[unknown]");
577 } else
578 user = xstrdup("");
579 if (c->session != NULL && state->event.key != KEYC_NONE) {
580 key = key_string_lookup_key(state->event.key, 0);
581 server_add_message("%s%s key %s: %s", c->name, user,
582 key, tmp);
583 } else {
584 server_add_message("%s%s command: %s", c->name, user,
585 tmp);
587 free(user);
588 } else
589 server_add_message("command: %s", tmp);
590 free(tmp);
593 /* Fire command on command queue. */
594 static enum cmd_retval
595 cmdq_fire_command(struct cmdq_item *item)
597 const char *name = cmdq_name(item->client);
598 struct cmdq_state *state = item->state;
599 struct cmd *cmd = item->cmd;
600 struct args *args = cmd_get_args(cmd);
601 const struct cmd_entry *entry = cmd_get_entry(cmd);
602 struct client *tc, *saved = item->client;
603 enum cmd_retval retval;
604 struct cmd_find_state *fsp, fs;
605 int flags, quiet = 0;
606 char *tmp;
608 if (cfg_finished)
609 cmdq_add_message(item);
610 if (log_get_level() > 1) {
611 tmp = cmd_print(cmd);
612 log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
613 free(tmp);
616 flags = !!(state->flags & CMDQ_STATE_CONTROL);
617 cmdq_guard(item, "begin", flags);
619 if (item->client == NULL)
620 item->client = cmd_find_client(item, NULL, 1);
622 if (entry->flags & CMD_CLIENT_CANFAIL)
623 quiet = 1;
624 if (entry->flags & CMD_CLIENT_CFLAG) {
625 tc = cmd_find_client(item, args_get(args, 'c'), quiet);
626 if (tc == NULL && !quiet) {
627 retval = CMD_RETURN_ERROR;
628 goto out;
630 } else if (entry->flags & CMD_CLIENT_TFLAG) {
631 tc = cmd_find_client(item, args_get(args, 't'), quiet);
632 if (tc == NULL && !quiet) {
633 retval = CMD_RETURN_ERROR;
634 goto out;
636 } else
637 tc = cmd_find_client(item, NULL, 1);
638 item->target_client = tc;
640 retval = cmdq_find_flag(item, &item->source, &entry->source);
641 if (retval == CMD_RETURN_ERROR)
642 goto out;
643 retval = cmdq_find_flag(item, &item->target, &entry->target);
644 if (retval == CMD_RETURN_ERROR)
645 goto out;
647 retval = entry->exec(cmd, item);
648 if (retval == CMD_RETURN_ERROR)
649 goto out;
651 if (entry->flags & CMD_AFTERHOOK) {
652 if (cmd_find_valid_state(&item->target))
653 fsp = &item->target;
654 else if (cmd_find_valid_state(&item->state->current))
655 fsp = &item->state->current;
656 else if (cmd_find_from_client(&fs, item->client, 0) == 0)
657 fsp = &fs;
658 else
659 goto out;
660 cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
663 out:
664 item->client = saved;
665 if (retval == CMD_RETURN_ERROR)
666 cmdq_guard(item, "error", flags);
667 else
668 cmdq_guard(item, "end", flags);
669 return (retval);
672 /* Get a callback for the command queue. */
673 struct cmdq_item *
674 cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
676 struct cmdq_item *item;
678 item = xcalloc(1, sizeof *item);
679 xasprintf(&item->name, "[%s/%p]", name, item);
680 item->type = CMDQ_CALLBACK;
682 item->group = 0;
683 item->state = cmdq_new_state(NULL, NULL, 0);
685 item->cb = cb;
686 item->data = data;
688 return (item);
691 /* Generic error callback. */
692 static enum cmd_retval
693 cmdq_error_callback(struct cmdq_item *item, void *data)
695 char *error = data;
697 cmdq_error(item, "%s", error);
698 free(error);
700 return (CMD_RETURN_NORMAL);
703 /* Get an error callback for the command queue. */
704 struct cmdq_item *
705 cmdq_get_error(const char *error)
707 return (cmdq_get_callback(cmdq_error_callback, xstrdup(error)));
710 /* Fire callback on callback queue. */
711 static enum cmd_retval
712 cmdq_fire_callback(struct cmdq_item *item)
714 return (item->cb(item, item->data));
717 /* Process next item on command queue. */
718 u_int
719 cmdq_next(struct client *c)
721 struct cmdq_list *queue = cmdq_get(c);
722 const char *name = cmdq_name(c);
723 struct cmdq_item *item;
724 enum cmd_retval retval;
725 u_int items = 0;
726 static u_int number;
728 if (TAILQ_EMPTY(&queue->list)) {
729 log_debug("%s %s: empty", __func__, name);
730 return (0);
732 if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) {
733 log_debug("%s %s: waiting", __func__, name);
734 return (0);
737 log_debug("%s %s: enter", __func__, name);
738 for (;;) {
739 item = queue->item = TAILQ_FIRST(&queue->list);
740 if (item == NULL)
741 break;
742 log_debug("%s %s: %s (%d), flags %x", __func__, name,
743 item->name, item->type, item->flags);
746 * Any item with the waiting flag set waits until an external
747 * event clears the flag (for example, a job - look at
748 * run-shell).
750 if (item->flags & CMDQ_WAITING)
751 goto waiting;
754 * Items are only fired once, once the fired flag is set, a
755 * waiting flag can only be cleared by an external event.
757 if (~item->flags & CMDQ_FIRED) {
758 item->time = time(NULL);
759 item->number = ++number;
761 switch (item->type) {
762 case CMDQ_COMMAND:
763 retval = cmdq_fire_command(item);
766 * If a command returns an error, remove any
767 * subsequent commands in the same group.
769 if (retval == CMD_RETURN_ERROR)
770 cmdq_remove_group(item);
771 break;
772 case CMDQ_CALLBACK:
773 retval = cmdq_fire_callback(item);
774 break;
775 default:
776 retval = CMD_RETURN_ERROR;
777 break;
779 item->flags |= CMDQ_FIRED;
781 if (retval == CMD_RETURN_WAIT) {
782 item->flags |= CMDQ_WAITING;
783 goto waiting;
785 items++;
787 cmdq_remove(item);
789 queue->item = NULL;
791 log_debug("%s %s: exit (empty)", __func__, name);
792 return (items);
794 waiting:
795 log_debug("%s %s: exit (wait)", __func__, name);
796 return (items);
799 /* Get running item if any. */
800 struct cmdq_item *
801 cmdq_running(struct client *c)
803 struct cmdq_list *queue = cmdq_get(c);
805 if (queue->item == NULL)
806 return (NULL);
807 if (queue->item->flags & CMDQ_WAITING)
808 return (NULL);
809 return (queue->item);
812 /* Print a guard line. */
813 void
814 cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
816 struct client *c = item->client;
817 long t = item->time;
818 u_int number = item->number;
820 if (c != NULL && (c->flags & CLIENT_CONTROL))
821 control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
824 /* Show message from command. */
825 void
826 cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb)
828 server_client_print(item->client, parse, evb);
831 /* Show message from command. */
832 void
833 cmdq_print(struct cmdq_item *item, const char *fmt, ...)
835 va_list ap;
836 struct evbuffer *evb;
838 evb = evbuffer_new();
839 if (evb == NULL)
840 fatalx("out of memory");
842 va_start(ap, fmt);
843 evbuffer_add_vprintf(evb, fmt, ap);
844 va_end(ap);
846 cmdq_print_data(item, 0, evb);
847 evbuffer_free(evb);
850 /* Show error from command. */
851 void
852 cmdq_error(struct cmdq_item *item, const char *fmt, ...)
854 struct client *c = item->client;
855 struct cmd *cmd = item->cmd;
856 va_list ap;
857 char *msg, *tmp;
858 const char *file;
859 u_int line;
861 va_start(ap, fmt);
862 xvasprintf(&msg, fmt, ap);
863 va_end(ap);
865 log_debug("%s: %s", __func__, msg);
867 if (c == NULL) {
868 cmd_get_source(cmd, &file, &line);
869 cfg_add_cause("%s:%u: %s", file, line, msg);
870 } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
871 server_add_message("%s message: %s", c->name, msg);
872 if (~c->flags & CLIENT_UTF8) {
873 tmp = msg;
874 msg = utf8_sanitize(tmp);
875 free(tmp);
877 if (c->flags & CLIENT_CONTROL)
878 control_write(c, "%s", msg);
879 else
880 file_error(c, "%s\n", msg);
881 c->retval = 1;
882 } else {
883 *msg = toupper((u_char) *msg);
884 status_message_set(c, -1, 1, 0, "%s", msg);
887 free(msg);