4 * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
31 /* Command queue flags. */
32 #define CMDQ_FIRED 0x1
33 #define CMDQ_WAITING 0x2
35 /* Command queue item type. */
41 /* Command queue item. */
44 struct cmdq_list
*queue
;
45 struct cmdq_item
*next
;
47 struct client
*client
;
48 struct client
*target_client
;
58 struct cmdq_state
*state
;
59 struct cmd_find_state source
;
60 struct cmd_find_state target
;
62 struct cmd_list
*cmdlist
;
68 TAILQ_ENTRY(cmdq_item
) entry
;
70 TAILQ_HEAD(cmdq_item_list
, cmdq_item
);
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
83 struct format_tree
*formats
;
85 struct key_event event
;
86 struct cmd_find_state current
;
91 struct cmdq_item
*item
;
92 struct cmdq_item_list list
;
95 /* Get command queue name. */
97 cmdq_name(struct client
*c
)
104 xsnprintf(s
, sizeof s
, "<%s>", c
->name
);
106 xsnprintf(s
, sizeof s
, "<%p>", c
);
110 /* Get command queue from client. */
111 static struct cmdq_list
*
112 cmdq_get(struct client
*c
)
114 static struct cmdq_list
*global_queue
;
117 if (global_queue
== NULL
)
118 global_queue
= cmdq_new();
119 return (global_queue
);
124 /* Create a queue. */
128 struct cmdq_list
*queue
;
130 queue
= xcalloc(1, sizeof *queue
);
131 TAILQ_INIT (&queue
->list
);
137 cmdq_free(struct cmdq_list
*queue
)
139 if (!TAILQ_EMPTY(&queue
->list
))
140 fatalx("queue not empty");
146 cmdq_get_name(struct cmdq_item
*item
)
151 /* Get item client. */
153 cmdq_get_client(struct cmdq_item
*item
)
155 return (item
->client
);
158 /* Get item target client. */
160 cmdq_get_target_client(struct cmdq_item
*item
)
162 return (item
->target_client
);
165 /* Get item state. */
167 cmdq_get_state(struct cmdq_item
*item
)
169 return (item
->state
);
172 /* Get item target. */
173 struct cmd_find_state
*
174 cmdq_get_target(struct cmdq_item
*item
)
176 return (&item
->target
);
179 /* Get item source. */
180 struct cmd_find_state
*
181 cmdq_get_source(struct cmdq_item
*item
)
183 return (&item
->source
);
186 /* Get state event. */
188 cmdq_get_event(struct cmdq_item
*item
)
190 return (&item
->state
->event
);
193 /* Get state current target. */
194 struct cmd_find_state
*
195 cmdq_get_current(struct cmdq_item
*item
)
197 return (&item
->state
->current
);
200 /* Get state flags. */
202 cmdq_get_flags(struct cmdq_item
*item
)
204 return (item
->state
->flags
);
207 /* Create a new state. */
209 cmdq_new_state(struct cmd_find_state
*current
, struct key_event
*event
,
212 struct cmdq_state
*state
;
214 state
= xcalloc(1, sizeof *state
);
215 state
->references
= 1;
216 state
->flags
= flags
;
219 memcpy(&state
->event
, event
, sizeof state
->event
);
221 state
->event
.key
= KEYC_NONE
;
222 if (current
!= NULL
&& cmd_find_valid_state(current
))
223 cmd_find_copy_state(&state
->current
, current
);
225 cmd_find_clear_state(&state
->current
, 0);
230 /* Add a reference to a state. */
232 cmdq_link_state(struct cmdq_state
*state
)
238 /* Make a copy of a state. */
240 cmdq_copy_state(struct cmdq_state
*state
, struct cmd_find_state
*current
)
243 return (cmdq_new_state(current
, &state
->event
, state
->flags
));
244 return (cmdq_new_state(&state
->current
, &state
->event
, state
->flags
));
249 cmdq_free_state(struct cmdq_state
*state
)
251 if (--state
->references
!= 0)
254 if (state
->formats
!= NULL
)
255 format_free(state
->formats
);
259 /* Add a format to command queue. */
261 cmdq_add_format(struct cmdq_state
*state
, const char *key
, const char *fmt
, ...)
267 xvasprintf(&value
, fmt
, ap
);
270 if (state
->formats
== NULL
)
271 state
->formats
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
272 format_add(state
->formats
, key
, "%s", value
);
277 /* Add formats to command queue. */
279 cmdq_add_formats(struct cmdq_state
*state
, struct format_tree
*ft
)
281 if (state
->formats
== NULL
)
282 state
->formats
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
283 format_merge(state
->formats
, ft
);
286 /* Merge formats from item. */
288 cmdq_merge_formats(struct cmdq_item
*item
, struct format_tree
*ft
)
290 const struct cmd_entry
*entry
;
292 if (item
->cmd
!= NULL
) {
293 entry
= cmd_get_entry(item
->cmd
);
294 format_add(ft
, "command", "%s", entry
->name
);
296 if (item
->state
->formats
!= NULL
)
297 format_merge(ft
, item
->state
->formats
);
300 /* Append an item. */
302 cmdq_append(struct client
*c
, struct cmdq_item
*item
)
304 struct cmdq_list
*queue
= cmdq_get(c
);
305 struct cmdq_item
*next
;
316 TAILQ_INSERT_TAIL(&queue
->list
, item
, entry
);
317 log_debug("%s %s: %s", __func__
, cmdq_name(c
), item
->name
);
320 } while (item
!= NULL
);
321 return (TAILQ_LAST(&queue
->list
, cmdq_item_list
));
324 /* Insert an item. */
326 cmdq_insert_after(struct cmdq_item
*after
, struct cmdq_item
*item
)
328 struct client
*c
= after
->client
;
329 struct cmdq_list
*queue
= after
->queue
;
330 struct cmdq_item
*next
;
334 item
->next
= after
->next
;
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
);
348 } while (item
!= NULL
);
354 cmdq_insert_hook(struct session
*s
, struct cmdq_item
*item
,
355 struct cmd_find_state
*current
, const char *fmt
, ...)
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
;
364 char *name
, tmp
[32], flag
, *arguments
;
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
;
373 if (item
->state
->flags
& CMDQ_STATE_NOHOOKS
)
376 oo
= global_s_options
;
381 xvasprintf(&name
, fmt
, ap
);
384 o
= options_get(oo
, name
);
389 log_debug("running hook %s (parent %p)", name
, item
);
392 * The hooks get a new state because they should not update the current
393 * target or formats for any subsequent commands.
395 new_state
= cmdq_new_state(current
, &state
->event
, CMDQ_STATE_NOHOOKS
);
396 cmdq_add_format(new_state
, "hook", "%s", name
);
398 arguments
= args_print(args
);
399 cmdq_add_format(new_state
, "hook_arguments", "%s", arguments
);
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
));
406 flag
= args_first(args
, &ae
);
408 value
= args_get(args
, flag
);
410 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c", flag
);
411 cmdq_add_format(new_state
, tmp
, "1");
413 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c", flag
);
414 cmdq_add_format(new_state
, tmp
, "%s", value
);
418 av
= args_first_value(args
, flag
);
420 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c_%d", flag
, i
);
421 cmdq_add_format(new_state
, tmp
, "%s", av
->string
);
423 av
= args_next_value(av
);
426 flag
= args_next(&ae
);
429 a
= options_array_first(o
);
431 cmdlist
= options_array_item_value(a
)->cmdlist
;
432 if (cmdlist
!= NULL
) {
433 new_item
= cmdq_get_command(cmdlist
, new_state
);
435 item
= cmdq_insert_after(item
, new_item
);
437 item
= cmdq_append(NULL
, new_item
);
439 a
= options_array_next(a
);
442 cmdq_free_state(new_state
);
446 /* Continue processing command queue. */
448 cmdq_continue(struct cmdq_item
*item
)
450 item
->flags
&= ~CMDQ_WAITING
;
453 /* Remove an item. */
455 cmdq_remove(struct cmdq_item
*item
)
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
);
463 TAILQ_REMOVE(&item
->queue
->list
, item
, entry
);
469 /* Remove all subsequent items that match this item's group. */
471 cmdq_remove_group(struct cmdq_item
*item
)
473 struct cmdq_item
*this, *next
;
475 if (item
->group
== 0)
477 this = TAILQ_NEXT(item
, entry
);
478 while (this != NULL
) {
479 next
= TAILQ_NEXT(this, entry
);
480 if (this->group
== item
->group
)
486 /* Empty command callback. */
487 static enum cmd_retval
488 cmdq_empty_command(__unused
struct cmdq_item
*item
, __unused
void *data
)
490 return (CMD_RETURN_NORMAL
);
493 /* Get a command for the command queue. */
495 cmdq_get_command(struct cmd_list
*cmdlist
, struct cmdq_state
*state
)
497 struct cmdq_item
*item
, *first
= NULL
, *last
= NULL
;
499 const struct cmd_entry
*entry
;
502 if ((cmd
= cmd_list_first(cmdlist
)) == NULL
)
503 return (cmdq_get_callback(cmdq_empty_command
, NULL
));
506 state
= cmdq_new_state(NULL
, NULL
, 0);
510 while (cmd
!= NULL
) {
511 entry
= cmd_get_entry(cmd
);
513 item
= xcalloc(1, sizeof *item
);
514 xasprintf(&item
->name
, "[%s/%p]", entry
->name
, item
);
515 item
->type
= CMDQ_COMMAND
;
517 item
->group
= cmd_get_group(cmd
);
518 item
->state
= cmdq_link_state(state
);
520 item
->cmdlist
= cmdlist
;
523 cmdlist
->references
++;
524 log_debug("%s: %s group %u", __func__
, item
->name
, item
->group
);
532 cmd
= cmd_list_next(cmd
);
536 cmdq_free_state(state
);
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
)
547 if (flag
->flag
== 0) {
548 cmd_find_from_client(fs
, item
->target_client
, 0);
549 return (CMD_RETURN_NORMAL
);
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
);
557 return (CMD_RETURN_NORMAL
);
560 /* Add message with command. */
562 cmdq_add_message(struct cmdq_item
*item
)
564 struct client
*c
= item
->client
;
565 struct cmdq_state
*state
= item
->state
;
572 tmp
= cmd_print(item
->cmd
);
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
);
579 user
= xstrdup("[unknown]");
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
,
587 server_add_message("%s%s command: %s", c
->name
, user
,
592 server_add_message("command: %s", tmp
);
596 /* Fire command on command queue. */
597 static enum cmd_retval
598 cmdq_fire_command(struct cmdq_item
*item
)
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;
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
);
619 flags
= !!(state
->flags
& CMDQ_STATE_CONTROL
);
620 cmdq_guard(item
, "begin", flags
);
622 if (item
->client
== NULL
)
623 item
->client
= cmd_find_client(item
, NULL
, 1);
625 if (entry
->flags
& CMD_CLIENT_CANFAIL
)
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
;
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
;
640 tc
= cmd_find_client(item
, NULL
, 1);
641 item
->target_client
= tc
;
643 retval
= cmdq_find_flag(item
, &item
->source
, &entry
->source
);
644 if (retval
== CMD_RETURN_ERROR
)
646 retval
= cmdq_find_flag(item
, &item
->target
, &entry
->target
);
647 if (retval
== CMD_RETURN_ERROR
)
650 retval
= entry
->exec(cmd
, item
);
651 if (retval
== CMD_RETURN_ERROR
)
654 if (entry
->flags
& CMD_AFTERHOOK
) {
655 if (cmd_find_valid_state(&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)
663 cmdq_insert_hook(fsp
->s
, item
, fsp
, "after-%s", entry
->name
);
667 item
->client
= saved
;
668 if (retval
== CMD_RETURN_ERROR
)
669 cmdq_guard(item
, "error", flags
);
671 cmdq_guard(item
, "end", flags
);
675 /* Get a callback for the command queue. */
677 cmdq_get_callback1(const char *name
, cmdq_cb cb
, void *data
)
679 struct cmdq_item
*item
;
681 item
= xcalloc(1, sizeof *item
);
682 xasprintf(&item
->name
, "[%s/%p]", name
, item
);
683 item
->type
= CMDQ_CALLBACK
;
686 item
->state
= cmdq_new_state(NULL
, NULL
, 0);
694 /* Generic error callback. */
695 static enum cmd_retval
696 cmdq_error_callback(struct cmdq_item
*item
, void *data
)
700 cmdq_error(item
, "%s", error
);
703 return (CMD_RETURN_NORMAL
);
706 /* Get an error callback for the command queue. */
708 cmdq_get_error(const char *error
)
710 return (cmdq_get_callback(cmdq_error_callback
, xstrdup(error
)));
713 /* Fire callback on callback queue. */
714 static enum cmd_retval
715 cmdq_fire_callback(struct cmdq_item
*item
)
717 return (item
->cb(item
, item
->data
));
720 /* Process next item on command queue. */
722 cmdq_next(struct client
*c
)
724 struct cmdq_list
*queue
= cmdq_get(c
);
725 const char *name
= cmdq_name(c
);
726 struct cmdq_item
*item
;
727 enum cmd_retval retval
;
731 if (TAILQ_EMPTY(&queue
->list
)) {
732 log_debug("%s %s: empty", __func__
, name
);
735 if (TAILQ_FIRST(&queue
->list
)->flags
& CMDQ_WAITING
) {
736 log_debug("%s %s: waiting", __func__
, name
);
740 log_debug("%s %s: enter", __func__
, name
);
742 item
= queue
->item
= TAILQ_FIRST(&queue
->list
);
745 log_debug("%s %s: %s (%d), flags %x", __func__
, name
,
746 item
->name
, item
->type
, item
->flags
);
749 * Any item with the waiting flag set waits until an external
750 * event clears the flag (for example, a job - look at
753 if (item
->flags
& CMDQ_WAITING
)
757 * Items are only fired once, once the fired flag is set, a
758 * waiting flag can only be cleared by an external event.
760 if (~item
->flags
& CMDQ_FIRED
) {
761 item
->time
= time(NULL
);
762 item
->number
= ++number
;
764 switch (item
->type
) {
766 retval
= cmdq_fire_command(item
);
769 * If a command returns an error, remove any
770 * subsequent commands in the same group.
772 if (retval
== CMD_RETURN_ERROR
)
773 cmdq_remove_group(item
);
776 retval
= cmdq_fire_callback(item
);
779 retval
= CMD_RETURN_ERROR
;
782 item
->flags
|= CMDQ_FIRED
;
784 if (retval
== CMD_RETURN_WAIT
) {
785 item
->flags
|= CMDQ_WAITING
;
794 log_debug("%s %s: exit (empty)", __func__
, name
);
798 log_debug("%s %s: exit (wait)", __func__
, name
);
802 /* Get running item if any. */
804 cmdq_running(struct client
*c
)
806 struct cmdq_list
*queue
= cmdq_get(c
);
808 if (queue
->item
== NULL
)
810 if (queue
->item
->flags
& CMDQ_WAITING
)
812 return (queue
->item
);
815 /* Print a guard line. */
817 cmdq_guard(struct cmdq_item
*item
, const char *guard
, int flags
)
819 struct client
*c
= item
->client
;
821 u_int number
= item
->number
;
823 if (c
!= NULL
&& (c
->flags
& CLIENT_CONTROL
))
824 control_write(c
, "%%%s %ld %u %d", guard
, t
, number
, flags
);
827 /* Show message from command. */
829 cmdq_print_data(struct cmdq_item
*item
, int parse
, struct evbuffer
*evb
)
831 server_client_print(item
->client
, parse
, evb
);
834 /* Show message from command. */
836 cmdq_print(struct cmdq_item
*item
, const char *fmt
, ...)
839 struct evbuffer
*evb
;
841 evb
= evbuffer_new();
843 fatalx("out of memory");
846 evbuffer_add_vprintf(evb
, fmt
, ap
);
849 cmdq_print_data(item
, 0, evb
);
853 /* Show error from command. */
855 cmdq_error(struct cmdq_item
*item
, const char *fmt
, ...)
857 struct client
*c
= item
->client
;
858 struct cmd
*cmd
= item
->cmd
;
865 xvasprintf(&msg
, fmt
, ap
);
868 log_debug("%s: %s", __func__
, msg
);
871 cmd_get_source(cmd
, &file
, &line
);
872 cfg_add_cause("%s:%u: %s", file
, line
, msg
);
873 } else if (c
->session
== NULL
|| (c
->flags
& CLIENT_CONTROL
)) {
874 server_add_message("%s message: %s", c
->name
, msg
);
875 if (~c
->flags
& CLIENT_UTF8
) {
877 msg
= utf8_sanitize(tmp
);
880 if (c
->flags
& CLIENT_CONTROL
)
881 control_write(c
, "%s", msg
);
883 file_error(c
, "%s\n", msg
);
886 *msg
= toupper((u_char
) *msg
);
887 status_message_set(c
, -1, 1, 0, "%s", msg
);