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