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>
30 /* Command queue flags. */
31 #define CMDQ_FIRED 0x1
32 #define CMDQ_WAITING 0x2
34 /* Command queue item type. */
40 /* Command queue item. */
43 struct cmdq_list
*queue
;
44 struct cmdq_item
*next
;
46 struct client
*client
;
47 struct client
*target_client
;
57 struct cmdq_state
*state
;
58 struct cmd_find_state source
;
59 struct cmd_find_state target
;
61 struct cmd_list
*cmdlist
;
67 TAILQ_ENTRY(cmdq_item
) entry
;
69 TAILQ_HEAD(cmdq_item_list
, cmdq_item
);
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
82 struct format_tree
*formats
;
84 struct key_event event
;
85 struct cmd_find_state current
;
90 struct cmdq_item
*item
;
91 struct cmdq_item_list list
;
94 /* Get command queue name. */
96 cmdq_name(struct client
*c
)
103 xsnprintf(s
, sizeof s
, "<%s>", c
->name
);
105 xsnprintf(s
, sizeof s
, "<%p>", c
);
109 /* Get command queue from client. */
110 static struct cmdq_list
*
111 cmdq_get(struct client
*c
)
113 static struct cmdq_list
*global_queue
;
116 if (global_queue
== NULL
)
117 global_queue
= cmdq_new();
118 return (global_queue
);
123 /* Create a queue. */
127 struct cmdq_list
*queue
;
129 queue
= xcalloc(1, sizeof *queue
);
130 TAILQ_INIT (&queue
->list
);
136 cmdq_free(struct cmdq_list
*queue
)
138 if (!TAILQ_EMPTY(&queue
->list
))
139 fatalx("queue not empty");
145 cmdq_get_name(struct cmdq_item
*item
)
150 /* Get item client. */
152 cmdq_get_client(struct cmdq_item
*item
)
154 return (item
->client
);
157 /* Get item target client. */
159 cmdq_get_target_client(struct cmdq_item
*item
)
161 return (item
->target_client
);
164 /* Get item state. */
166 cmdq_get_state(struct cmdq_item
*item
)
168 return (item
->state
);
171 /* Get item target. */
172 struct cmd_find_state
*
173 cmdq_get_target(struct cmdq_item
*item
)
175 return (&item
->target
);
178 /* Get item source. */
179 struct cmd_find_state
*
180 cmdq_get_source(struct cmdq_item
*item
)
182 return (&item
->source
);
185 /* Get state event. */
187 cmdq_get_event(struct cmdq_item
*item
)
189 return (&item
->state
->event
);
192 /* Get state current target. */
193 struct cmd_find_state
*
194 cmdq_get_current(struct cmdq_item
*item
)
196 return (&item
->state
->current
);
199 /* Get state flags. */
201 cmdq_get_flags(struct cmdq_item
*item
)
203 return (item
->state
->flags
);
206 /* Create a new state. */
208 cmdq_new_state(struct cmd_find_state
*current
, struct key_event
*event
,
211 struct cmdq_state
*state
;
213 state
= xcalloc(1, sizeof *state
);
214 state
->references
= 1;
215 state
->flags
= flags
;
218 memcpy(&state
->event
, event
, sizeof state
->event
);
220 state
->event
.key
= KEYC_NONE
;
221 if (current
!= NULL
&& cmd_find_valid_state(current
))
222 cmd_find_copy_state(&state
->current
, current
);
224 cmd_find_clear_state(&state
->current
, 0);
229 /* Add a reference to a state. */
231 cmdq_link_state(struct cmdq_state
*state
)
237 /* Make a copy of a state. */
239 cmdq_copy_state(struct cmdq_state
*state
, struct cmd_find_state
*current
)
242 return (cmdq_new_state(current
, &state
->event
, state
->flags
));
243 return (cmdq_new_state(&state
->current
, &state
->event
, state
->flags
));
248 cmdq_free_state(struct cmdq_state
*state
)
250 if (--state
->references
!= 0)
253 if (state
->formats
!= NULL
)
254 format_free(state
->formats
);
258 /* Add a format to command queue. */
260 cmdq_add_format(struct cmdq_state
*state
, const char *key
, const char *fmt
, ...)
266 xvasprintf(&value
, fmt
, ap
);
269 if (state
->formats
== NULL
)
270 state
->formats
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
271 format_add(state
->formats
, key
, "%s", value
);
276 /* Add formats to command queue. */
278 cmdq_add_formats(struct cmdq_state
*state
, struct format_tree
*ft
)
280 if (state
->formats
== NULL
)
281 state
->formats
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
282 format_merge(state
->formats
, ft
);
285 /* Merge formats from item. */
287 cmdq_merge_formats(struct cmdq_item
*item
, struct format_tree
*ft
)
289 const struct cmd_entry
*entry
;
291 if (item
->cmd
!= NULL
) {
292 entry
= cmd_get_entry(item
->cmd
);
293 format_add(ft
, "command", "%s", entry
->name
);
295 if (item
->state
->formats
!= NULL
)
296 format_merge(ft
, item
->state
->formats
);
299 /* Append an item. */
301 cmdq_append(struct client
*c
, struct cmdq_item
*item
)
303 struct cmdq_list
*queue
= cmdq_get(c
);
304 struct cmdq_item
*next
;
315 TAILQ_INSERT_TAIL(&queue
->list
, item
, entry
);
316 log_debug("%s %s: %s", __func__
, cmdq_name(c
), item
->name
);
319 } while (item
!= NULL
);
320 return (TAILQ_LAST(&queue
->list
, cmdq_item_list
));
323 /* Insert an item. */
325 cmdq_insert_after(struct cmdq_item
*after
, struct cmdq_item
*item
)
327 struct client
*c
= after
->client
;
328 struct cmdq_list
*queue
= after
->queue
;
329 struct cmdq_item
*next
;
333 item
->next
= after
->next
;
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
);
347 } while (item
!= NULL
);
353 cmdq_insert_hook(struct session
*s
, struct cmdq_item
*item
,
354 struct cmd_find_state
*current
, const char *fmt
, ...)
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
;
363 char *name
, tmp
[32], flag
, *arguments
;
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
;
372 if (item
->state
->flags
& CMDQ_STATE_NOHOOKS
)
375 oo
= global_s_options
;
380 xvasprintf(&name
, fmt
, ap
);
383 o
= options_get(oo
, name
);
388 log_debug("running hook %s (parent %p)", name
, item
);
391 * The hooks get a new state because they should not update the current
392 * target or formats for any subsequent commands.
394 new_state
= cmdq_new_state(current
, &state
->event
, CMDQ_STATE_NOHOOKS
);
395 cmdq_add_format(new_state
, "hook", "%s", name
);
397 arguments
= args_print(args
);
398 cmdq_add_format(new_state
, "hook_arguments", "%s", arguments
);
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
));
405 flag
= args_first(args
, &ae
);
407 value
= args_get(args
, flag
);
409 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c", flag
);
410 cmdq_add_format(new_state
, tmp
, "1");
412 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c", flag
);
413 cmdq_add_format(new_state
, tmp
, "%s", value
);
417 av
= args_first_value(args
, flag
);
419 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c_%d", flag
, i
);
420 cmdq_add_format(new_state
, tmp
, "%s", av
->string
);
422 av
= args_next_value(av
);
425 flag
= args_next(&ae
);
428 a
= options_array_first(o
);
430 cmdlist
= options_array_item_value(a
)->cmdlist
;
431 if (cmdlist
!= NULL
) {
432 new_item
= cmdq_get_command(cmdlist
, new_state
);
434 item
= cmdq_insert_after(item
, new_item
);
436 item
= cmdq_append(NULL
, new_item
);
438 a
= options_array_next(a
);
441 cmdq_free_state(new_state
);
445 /* Continue processing command queue. */
447 cmdq_continue(struct cmdq_item
*item
)
449 item
->flags
&= ~CMDQ_WAITING
;
452 /* Remove an item. */
454 cmdq_remove(struct cmdq_item
*item
)
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
);
462 TAILQ_REMOVE(&item
->queue
->list
, item
, entry
);
468 /* Remove all subsequent items that match this item's group. */
470 cmdq_remove_group(struct cmdq_item
*item
)
472 struct cmdq_item
*this, *next
;
474 if (item
->group
== 0)
476 this = TAILQ_NEXT(item
, entry
);
477 while (this != NULL
) {
478 next
= TAILQ_NEXT(this, entry
);
479 if (this->group
== item
->group
)
485 /* Empty command callback. */
486 static enum cmd_retval
487 cmdq_empty_command(__unused
struct cmdq_item
*item
, __unused
void *data
)
489 return (CMD_RETURN_NORMAL
);
492 /* Get a command for the command queue. */
494 cmdq_get_command(struct cmd_list
*cmdlist
, struct cmdq_state
*state
)
496 struct cmdq_item
*item
, *first
= NULL
, *last
= NULL
;
498 const struct cmd_entry
*entry
;
501 if ((cmd
= cmd_list_first(cmdlist
)) == NULL
)
502 return (cmdq_get_callback(cmdq_empty_command
, NULL
));
505 state
= cmdq_new_state(NULL
, NULL
, 0);
509 while (cmd
!= NULL
) {
510 entry
= cmd_get_entry(cmd
);
512 item
= xcalloc(1, sizeof *item
);
513 xasprintf(&item
->name
, "[%s/%p]", entry
->name
, item
);
514 item
->type
= CMDQ_COMMAND
;
516 item
->group
= cmd_get_group(cmd
);
517 item
->state
= cmdq_link_state(state
);
519 item
->cmdlist
= cmdlist
;
522 cmdlist
->references
++;
523 log_debug("%s: %s group %u", __func__
, item
->name
, item
->group
);
531 cmd
= cmd_list_next(cmd
);
535 cmdq_free_state(state
);
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
)
546 if (flag
->flag
== 0) {
547 cmd_find_from_client(fs
, item
->target_client
, 0);
548 return (CMD_RETURN_NORMAL
);
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
);
556 return (CMD_RETURN_NORMAL
);
559 /* Add message with command. */
561 cmdq_add_message(struct cmdq_item
*item
)
563 struct client
*c
= item
->client
;
564 struct cmdq_state
*state
= item
->state
;
571 tmp
= cmd_print(item
->cmd
);
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
);
578 user
= xstrdup("[unknown]");
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
,
586 server_add_message("%s%s command: %s", c
->name
, user
,
591 server_add_message("command: %s", tmp
);
595 /* Fire command on command queue. */
596 static enum cmd_retval
597 cmdq_fire_command(struct cmdq_item
*item
)
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;
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
);
618 flags
= !!(state
->flags
& CMDQ_STATE_CONTROL
);
619 cmdq_guard(item
, "begin", flags
);
621 if (item
->client
== NULL
)
622 item
->client
= cmd_find_client(item
, NULL
, 1);
624 if (entry
->flags
& CMD_CLIENT_CANFAIL
)
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
;
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
;
639 tc
= cmd_find_client(item
, NULL
, 1);
640 item
->target_client
= tc
;
642 retval
= cmdq_find_flag(item
, &item
->source
, &entry
->source
);
643 if (retval
== CMD_RETURN_ERROR
)
645 retval
= cmdq_find_flag(item
, &item
->target
, &entry
->target
);
646 if (retval
== CMD_RETURN_ERROR
)
649 retval
= entry
->exec(cmd
, item
);
650 if (retval
== CMD_RETURN_ERROR
)
653 if (entry
->flags
& CMD_AFTERHOOK
) {
654 if (cmd_find_valid_state(&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)
662 cmdq_insert_hook(fsp
->s
, item
, fsp
, "after-%s", entry
->name
);
666 item
->client
= saved
;
667 if (retval
== CMD_RETURN_ERROR
) {
669 if (cmd_find_valid_state(&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)
675 cmdq_insert_hook(fsp
!= NULL
? fsp
->s
: NULL
, item
, fsp
,
677 cmdq_guard(item
, "error", flags
);
679 cmdq_guard(item
, "end", flags
);
683 /* Get a callback for the command queue. */
685 cmdq_get_callback1(const char *name
, cmdq_cb cb
, void *data
)
687 struct cmdq_item
*item
;
689 item
= xcalloc(1, sizeof *item
);
690 xasprintf(&item
->name
, "[%s/%p]", name
, item
);
691 item
->type
= CMDQ_CALLBACK
;
694 item
->state
= cmdq_new_state(NULL
, NULL
, 0);
702 /* Generic error callback. */
703 static enum cmd_retval
704 cmdq_error_callback(struct cmdq_item
*item
, void *data
)
708 cmdq_error(item
, "%s", error
);
711 return (CMD_RETURN_NORMAL
);
714 /* Get an error callback for the command queue. */
716 cmdq_get_error(const char *error
)
718 return (cmdq_get_callback(cmdq_error_callback
, xstrdup(error
)));
721 /* Fire callback on callback queue. */
722 static enum cmd_retval
723 cmdq_fire_callback(struct cmdq_item
*item
)
725 return (item
->cb(item
, item
->data
));
728 /* Process next item on command queue. */
730 cmdq_next(struct client
*c
)
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
;
739 if (TAILQ_EMPTY(&queue
->list
)) {
740 log_debug("%s %s: empty", __func__
, name
);
743 if (TAILQ_FIRST(&queue
->list
)->flags
& CMDQ_WAITING
) {
744 log_debug("%s %s: waiting", __func__
, name
);
748 log_debug("%s %s: enter", __func__
, name
);
750 item
= queue
->item
= TAILQ_FIRST(&queue
->list
);
753 log_debug("%s %s: %s (%d), flags %x", __func__
, name
,
754 item
->name
, item
->type
, item
->flags
);
757 * Any item with the waiting flag set waits until an external
758 * event clears the flag (for example, a job - look at
761 if (item
->flags
& CMDQ_WAITING
)
765 * Items are only fired once, once the fired flag is set, a
766 * waiting flag can only be cleared by an external event.
768 if (~item
->flags
& CMDQ_FIRED
) {
769 item
->time
= time(NULL
);
770 item
->number
= ++number
;
772 switch (item
->type
) {
774 retval
= cmdq_fire_command(item
);
777 * If a command returns an error, remove any
778 * subsequent commands in the same group.
780 if (retval
== CMD_RETURN_ERROR
)
781 cmdq_remove_group(item
);
784 retval
= cmdq_fire_callback(item
);
787 retval
= CMD_RETURN_ERROR
;
790 item
->flags
|= CMDQ_FIRED
;
792 if (retval
== CMD_RETURN_WAIT
) {
793 item
->flags
|= CMDQ_WAITING
;
802 log_debug("%s %s: exit (empty)", __func__
, name
);
806 log_debug("%s %s: exit (wait)", __func__
, name
);
810 /* Get running item if any. */
812 cmdq_running(struct client
*c
)
814 struct cmdq_list
*queue
= cmdq_get(c
);
816 if (queue
->item
== NULL
)
818 if (queue
->item
->flags
& CMDQ_WAITING
)
820 return (queue
->item
);
823 /* Print a guard line. */
825 cmdq_guard(struct cmdq_item
*item
, const char *guard
, int flags
)
827 struct client
*c
= item
->client
;
829 u_int number
= item
->number
;
831 if (c
!= NULL
&& (c
->flags
& CLIENT_CONTROL
))
832 control_write(c
, "%%%s %ld %u %d", guard
, t
, number
, flags
);
835 /* Show message from command. */
837 cmdq_print_data(struct cmdq_item
*item
, int parse
, struct evbuffer
*evb
)
839 server_client_print(item
->client
, parse
, evb
);
842 /* Show message from command. */
844 cmdq_print(struct cmdq_item
*item
, const char *fmt
, ...)
847 struct evbuffer
*evb
;
849 evb
= evbuffer_new();
851 fatalx("out of memory");
854 evbuffer_add_vprintf(evb
, fmt
, ap
);
857 cmdq_print_data(item
, 0, evb
);
861 /* Show error from command. */
863 cmdq_error(struct cmdq_item
*item
, const char *fmt
, ...)
865 struct client
*c
= item
->client
;
866 struct cmd
*cmd
= item
->cmd
;
873 xvasprintf(&msg
, fmt
, ap
);
876 log_debug("%s: %s", __func__
, msg
);
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
) {
885 msg
= utf8_sanitize(tmp
);
888 if (c
->flags
& CLIENT_CONTROL
)
889 control_write(c
, "%s", msg
);
891 file_error(c
, "%s\n", msg
);
894 *msg
= toupper((u_char
) *msg
);
895 status_message_set(c
, -1, 1, 0, "%s", msg
);