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