jcs's openbsd hax
openbsd
at jcs 900 lines 20 kB view raw
1/* $OpenBSD: cmd-queue.c,v 1.119 2025/02/10 08:14:32 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. */ 36enum cmdq_type { 37 CMDQ_COMMAND, 38 CMDQ_CALLBACK, 39}; 40 41/* Command queue item. */ 42struct 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}; 70TAILQ_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 */ 79struct 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. */ 90struct cmdq_list { 91 struct cmdq_item *item; 92 struct cmdq_item_list list; 93}; 94 95/* Get command queue name. */ 96static const char * 97cmdq_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. */ 111static struct cmdq_list * 112cmdq_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. */ 125struct cmdq_list * 126cmdq_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. */ 136void 137cmdq_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. */ 145const char * 146cmdq_get_name(struct cmdq_item *item) 147{ 148 return (item->name); 149} 150 151/* Get item client. */ 152struct client * 153cmdq_get_client(struct cmdq_item *item) 154{ 155 return (item->client); 156} 157 158/* Get item target client. */ 159struct client * 160cmdq_get_target_client(struct cmdq_item *item) 161{ 162 return (item->target_client); 163} 164 165/* Get item state. */ 166struct cmdq_state * 167cmdq_get_state(struct cmdq_item *item) 168{ 169 return (item->state); 170} 171 172/* Get item target. */ 173struct cmd_find_state * 174cmdq_get_target(struct cmdq_item *item) 175{ 176 return (&item->target); 177} 178 179/* Get item source. */ 180struct cmd_find_state * 181cmdq_get_source(struct cmdq_item *item) 182{ 183 return (&item->source); 184} 185 186/* Get state event. */ 187struct key_event * 188cmdq_get_event(struct cmdq_item *item) 189{ 190 return (&item->state->event); 191} 192 193/* Get state current target. */ 194struct cmd_find_state * 195cmdq_get_current(struct cmdq_item *item) 196{ 197 return (&item->state->current); 198} 199 200/* Get state flags. */ 201int 202cmdq_get_flags(struct cmdq_item *item) 203{ 204 return (item->state->flags); 205} 206 207/* Create a new state. */ 208struct cmdq_state * 209cmdq_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. */ 231struct cmdq_state * 232cmdq_link_state(struct cmdq_state *state) 233{ 234 state->references++; 235 return (state); 236} 237 238/* Make a copy of a state. */ 239struct cmdq_state * 240cmdq_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. */ 248void 249cmdq_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. */ 260void 261cmdq_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. */ 278void 279cmdq_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. */ 287void 288cmdq_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. */ 301struct cmdq_item * 302cmdq_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. */ 325struct cmdq_item * 326cmdq_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. */ 353void 354cmdq_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. */ 447void 448cmdq_continue(struct cmdq_item *item) 449{ 450 item->flags &= ~CMDQ_WAITING; 451} 452 453/* Remove an item. */ 454static void 455cmdq_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. */ 470static void 471cmdq_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. */ 487static enum cmd_retval 488cmdq_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. */ 494struct cmdq_item * 495cmdq_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. */ 541static enum cmd_retval 542cmdq_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. */ 561static void 562cmdq_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. */ 597static enum cmd_retval 598cmdq_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 666out: 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. */ 685struct cmdq_item * 686cmdq_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. */ 704static enum cmd_retval 705cmdq_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. */ 716struct cmdq_item * 717cmdq_get_error(const char *error) 718{ 719 return (cmdq_get_callback(cmdq_error_callback, xstrdup(error))); 720} 721 722/* Fire callback on callback queue. */ 723static enum cmd_retval 724cmdq_fire_callback(struct cmdq_item *item) 725{ 726 return (item->cb(item, item->data)); 727} 728 729/* Process next item on command queue. */ 730u_int 731cmdq_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 806waiting: 807 log_debug("%s %s: exit (wait)", __func__, name); 808 return (items); 809} 810 811/* Get running item if any. */ 812struct cmdq_item * 813cmdq_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. */ 825void 826cmdq_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. */ 837void 838cmdq_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. */ 844void 845cmdq_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. */ 863void 864cmdq_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, 0, "%s", msg); 897 } 898 899 free(msg); 900}