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