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
)
241 return (cmdq_new_state(&state
->current
, &state
->event
, state
->flags
));
246 cmdq_free_state(struct cmdq_state
*state
)
248 if (--state
->references
!= 0)
251 if (state
->formats
!= NULL
)
252 format_free(state
->formats
);
256 /* Add a format to command queue. */
258 cmdq_add_format(struct cmdq_state
*state
, const char *key
, const char *fmt
, ...)
264 xvasprintf(&value
, fmt
, ap
);
267 if (state
->formats
== NULL
)
268 state
->formats
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
269 format_add(state
->formats
, key
, "%s", value
);
274 /* Add formats to command queue. */
276 cmdq_add_formats(struct cmdq_state
*state
, struct format_tree
*ft
)
278 if (state
->formats
== NULL
)
279 state
->formats
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
280 format_merge(state
->formats
, ft
);
283 /* Merge formats from item. */
285 cmdq_merge_formats(struct cmdq_item
*item
, struct format_tree
*ft
)
287 const struct cmd_entry
*entry
;
289 if (item
->cmd
!= NULL
) {
290 entry
= cmd_get_entry(item
->cmd
);
291 format_add(ft
, "command", "%s", entry
->name
);
293 if (item
->state
->formats
!= NULL
)
294 format_merge(ft
, item
->state
->formats
);
297 /* Append an item. */
299 cmdq_append(struct client
*c
, struct cmdq_item
*item
)
301 struct cmdq_list
*queue
= cmdq_get(c
);
302 struct cmdq_item
*next
;
313 TAILQ_INSERT_TAIL(&queue
->list
, item
, entry
);
314 log_debug("%s %s: %s", __func__
, cmdq_name(c
), item
->name
);
317 } while (item
!= NULL
);
318 return (TAILQ_LAST(&queue
->list
, cmdq_item_list
));
321 /* Insert an item. */
323 cmdq_insert_after(struct cmdq_item
*after
, struct cmdq_item
*item
)
325 struct client
*c
= after
->client
;
326 struct cmdq_list
*queue
= after
->queue
;
327 struct cmdq_item
*next
;
331 item
->next
= after
->next
;
339 TAILQ_INSERT_AFTER(&queue
->list
, after
, item
, entry
);
340 log_debug("%s %s: %s after %s", __func__
, cmdq_name(c
),
341 item
->name
, after
->name
);
345 } while (item
!= NULL
);
351 cmdq_insert_hook(struct session
*s
, struct cmdq_item
*item
,
352 struct cmd_find_state
*current
, const char *fmt
, ...)
354 struct cmdq_state
*state
= item
->state
;
355 struct cmd
*cmd
= item
->cmd
;
356 struct args
*args
= cmd_get_args(cmd
);
357 struct args_entry
*ae
;
358 struct args_value
*av
;
361 char *name
, tmp
[32], flag
, *arguments
;
364 struct cmdq_item
*new_item
;
365 struct cmdq_state
*new_state
;
366 struct options_entry
*o
;
367 struct options_array_item
*a
;
368 struct cmd_list
*cmdlist
;
370 if (item
->state
->flags
& CMDQ_STATE_NOHOOKS
)
373 oo
= global_s_options
;
378 xvasprintf(&name
, fmt
, ap
);
381 o
= options_get(oo
, name
);
386 log_debug("running hook %s (parent %p)", name
, item
);
389 * The hooks get a new state because they should not update the current
390 * target or formats for any subsequent commands.
392 new_state
= cmdq_new_state(current
, &state
->event
, CMDQ_STATE_NOHOOKS
);
393 cmdq_add_format(new_state
, "hook", "%s", name
);
395 arguments
= args_print(args
);
396 cmdq_add_format(new_state
, "hook_arguments", "%s", arguments
);
399 for (i
= 0; i
< args_count(args
); i
++) {
400 xsnprintf(tmp
, sizeof tmp
, "hook_argument_%d", i
);
401 cmdq_add_format(new_state
, tmp
, "%s", args_string(args
, i
));
403 flag
= args_first(args
, &ae
);
405 value
= args_get(args
, flag
);
407 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c", flag
);
408 cmdq_add_format(new_state
, tmp
, "1");
410 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c", flag
);
411 cmdq_add_format(new_state
, tmp
, "%s", value
);
415 av
= args_first_value(args
, flag
);
417 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c_%d", flag
, i
);
418 cmdq_add_format(new_state
, tmp
, "%s", av
->string
);
420 av
= args_next_value(av
);
423 flag
= args_next(&ae
);
426 a
= options_array_first(o
);
428 cmdlist
= options_array_item_value(a
)->cmdlist
;
429 if (cmdlist
!= NULL
) {
430 new_item
= cmdq_get_command(cmdlist
, new_state
);
432 item
= cmdq_insert_after(item
, new_item
);
434 item
= cmdq_append(NULL
, new_item
);
436 a
= options_array_next(a
);
439 cmdq_free_state(new_state
);
443 /* Continue processing command queue. */
445 cmdq_continue(struct cmdq_item
*item
)
447 item
->flags
&= ~CMDQ_WAITING
;
450 /* Remove an item. */
452 cmdq_remove(struct cmdq_item
*item
)
454 if (item
->client
!= NULL
)
455 server_client_unref(item
->client
);
456 if (item
->cmdlist
!= NULL
)
457 cmd_list_free(item
->cmdlist
);
458 cmdq_free_state(item
->state
);
460 TAILQ_REMOVE(&item
->queue
->list
, item
, entry
);
466 /* Remove all subsequent items that match this item's group. */
468 cmdq_remove_group(struct cmdq_item
*item
)
470 struct cmdq_item
*this, *next
;
472 if (item
->group
== 0)
474 this = TAILQ_NEXT(item
, entry
);
475 while (this != NULL
) {
476 next
= TAILQ_NEXT(this, entry
);
477 if (this->group
== item
->group
)
483 /* Empty command callback. */
484 static enum cmd_retval
485 cmdq_empty_command(__unused
struct cmdq_item
*item
, __unused
void *data
)
487 return (CMD_RETURN_NORMAL
);
490 /* Get a command for the command queue. */
492 cmdq_get_command(struct cmd_list
*cmdlist
, struct cmdq_state
*state
)
494 struct cmdq_item
*item
, *first
= NULL
, *last
= NULL
;
496 const struct cmd_entry
*entry
;
499 if ((cmd
= cmd_list_first(cmdlist
)) == NULL
)
500 return (cmdq_get_callback(cmdq_empty_command
, NULL
));
503 state
= cmdq_new_state(NULL
, NULL
, 0);
507 while (cmd
!= NULL
) {
508 entry
= cmd_get_entry(cmd
);
510 item
= xcalloc(1, sizeof *item
);
511 xasprintf(&item
->name
, "[%s/%p]", entry
->name
, item
);
512 item
->type
= CMDQ_COMMAND
;
514 item
->group
= cmd_get_group(cmd
);
515 item
->state
= cmdq_link_state(state
);
517 item
->cmdlist
= cmdlist
;
520 cmdlist
->references
++;
521 log_debug("%s: %s group %u", __func__
, item
->name
, item
->group
);
529 cmd
= cmd_list_next(cmd
);
533 cmdq_free_state(state
);
537 /* Fill in flag for a command. */
538 static enum cmd_retval
539 cmdq_find_flag(struct cmdq_item
*item
, struct cmd_find_state
*fs
,
540 const struct cmd_entry_flag
*flag
)
544 if (flag
->flag
== 0) {
545 cmd_find_from_client(fs
, item
->target_client
, 0);
546 return (CMD_RETURN_NORMAL
);
549 value
= args_get(cmd_get_args(item
->cmd
), flag
->flag
);
550 if (cmd_find_target(fs
, item
, value
, flag
->type
, flag
->flags
) != 0) {
551 cmd_find_clear_state(fs
, 0);
552 return (CMD_RETURN_ERROR
);
554 return (CMD_RETURN_NORMAL
);
557 /* Add message with command. */
559 cmdq_add_message(struct cmdq_item
*item
)
561 struct client
*c
= item
->client
;
562 struct cmdq_state
*state
= item
->state
;
569 tmp
= cmd_print(item
->cmd
);
571 uid
= proc_get_peer_uid(c
->peer
);
572 if (uid
!= (uid_t
)-1 && uid
!= getuid()) {
573 if ((pw
= getpwuid(uid
)) != NULL
)
574 xasprintf(&user
, "[%s]", pw
->pw_name
);
576 user
= xstrdup("[unknown]");
579 if (c
->session
!= NULL
&& state
->event
.key
!= KEYC_NONE
) {
580 key
= key_string_lookup_key(state
->event
.key
, 0);
581 server_add_message("%s%s key %s: %s", c
->name
, user
,
584 server_add_message("%s%s command: %s", c
->name
, user
,
589 server_add_message("command: %s", tmp
);
593 /* Fire command on command queue. */
594 static enum cmd_retval
595 cmdq_fire_command(struct cmdq_item
*item
)
597 const char *name
= cmdq_name(item
->client
);
598 struct cmdq_state
*state
= item
->state
;
599 struct cmd
*cmd
= item
->cmd
;
600 struct args
*args
= cmd_get_args(cmd
);
601 const struct cmd_entry
*entry
= cmd_get_entry(cmd
);
602 struct client
*tc
, *saved
= item
->client
;
603 enum cmd_retval retval
;
604 struct cmd_find_state
*fsp
, fs
;
605 int flags
, quiet
= 0;
609 cmdq_add_message(item
);
610 if (log_get_level() > 1) {
611 tmp
= cmd_print(cmd
);
612 log_debug("%s %s: (%u) %s", __func__
, name
, item
->group
, tmp
);
616 flags
= !!(state
->flags
& CMDQ_STATE_CONTROL
);
617 cmdq_guard(item
, "begin", flags
);
619 if (item
->client
== NULL
)
620 item
->client
= cmd_find_client(item
, NULL
, 1);
622 if (entry
->flags
& CMD_CLIENT_CANFAIL
)
624 if (entry
->flags
& CMD_CLIENT_CFLAG
) {
625 tc
= cmd_find_client(item
, args_get(args
, 'c'), quiet
);
626 if (tc
== NULL
&& !quiet
) {
627 retval
= CMD_RETURN_ERROR
;
630 } else if (entry
->flags
& CMD_CLIENT_TFLAG
) {
631 tc
= cmd_find_client(item
, args_get(args
, 't'), quiet
);
632 if (tc
== NULL
&& !quiet
) {
633 retval
= CMD_RETURN_ERROR
;
637 tc
= cmd_find_client(item
, NULL
, 1);
638 item
->target_client
= tc
;
640 retval
= cmdq_find_flag(item
, &item
->source
, &entry
->source
);
641 if (retval
== CMD_RETURN_ERROR
)
643 retval
= cmdq_find_flag(item
, &item
->target
, &entry
->target
);
644 if (retval
== CMD_RETURN_ERROR
)
647 retval
= entry
->exec(cmd
, item
);
648 if (retval
== CMD_RETURN_ERROR
)
651 if (entry
->flags
& CMD_AFTERHOOK
) {
652 if (cmd_find_valid_state(&item
->target
))
654 else if (cmd_find_valid_state(&item
->state
->current
))
655 fsp
= &item
->state
->current
;
656 else if (cmd_find_from_client(&fs
, item
->client
, 0) == 0)
660 cmdq_insert_hook(fsp
->s
, item
, fsp
, "after-%s", entry
->name
);
664 item
->client
= saved
;
665 if (retval
== CMD_RETURN_ERROR
)
666 cmdq_guard(item
, "error", flags
);
668 cmdq_guard(item
, "end", flags
);
672 /* Get a callback for the command queue. */
674 cmdq_get_callback1(const char *name
, cmdq_cb cb
, void *data
)
676 struct cmdq_item
*item
;
678 item
= xcalloc(1, sizeof *item
);
679 xasprintf(&item
->name
, "[%s/%p]", name
, item
);
680 item
->type
= CMDQ_CALLBACK
;
683 item
->state
= cmdq_new_state(NULL
, NULL
, 0);
691 /* Generic error callback. */
692 static enum cmd_retval
693 cmdq_error_callback(struct cmdq_item
*item
, void *data
)
697 cmdq_error(item
, "%s", error
);
700 return (CMD_RETURN_NORMAL
);
703 /* Get an error callback for the command queue. */
705 cmdq_get_error(const char *error
)
707 return (cmdq_get_callback(cmdq_error_callback
, xstrdup(error
)));
710 /* Fire callback on callback queue. */
711 static enum cmd_retval
712 cmdq_fire_callback(struct cmdq_item
*item
)
714 return (item
->cb(item
, item
->data
));
717 /* Process next item on command queue. */
719 cmdq_next(struct client
*c
)
721 struct cmdq_list
*queue
= cmdq_get(c
);
722 const char *name
= cmdq_name(c
);
723 struct cmdq_item
*item
;
724 enum cmd_retval retval
;
728 if (TAILQ_EMPTY(&queue
->list
)) {
729 log_debug("%s %s: empty", __func__
, name
);
732 if (TAILQ_FIRST(&queue
->list
)->flags
& CMDQ_WAITING
) {
733 log_debug("%s %s: waiting", __func__
, name
);
737 log_debug("%s %s: enter", __func__
, name
);
739 item
= queue
->item
= TAILQ_FIRST(&queue
->list
);
742 log_debug("%s %s: %s (%d), flags %x", __func__
, name
,
743 item
->name
, item
->type
, item
->flags
);
746 * Any item with the waiting flag set waits until an external
747 * event clears the flag (for example, a job - look at
750 if (item
->flags
& CMDQ_WAITING
)
754 * Items are only fired once, once the fired flag is set, a
755 * waiting flag can only be cleared by an external event.
757 if (~item
->flags
& CMDQ_FIRED
) {
758 item
->time
= time(NULL
);
759 item
->number
= ++number
;
761 switch (item
->type
) {
763 retval
= cmdq_fire_command(item
);
766 * If a command returns an error, remove any
767 * subsequent commands in the same group.
769 if (retval
== CMD_RETURN_ERROR
)
770 cmdq_remove_group(item
);
773 retval
= cmdq_fire_callback(item
);
776 retval
= CMD_RETURN_ERROR
;
779 item
->flags
|= CMDQ_FIRED
;
781 if (retval
== CMD_RETURN_WAIT
) {
782 item
->flags
|= CMDQ_WAITING
;
791 log_debug("%s %s: exit (empty)", __func__
, name
);
795 log_debug("%s %s: exit (wait)", __func__
, name
);
799 /* Get running item if any. */
801 cmdq_running(struct client
*c
)
803 struct cmdq_list
*queue
= cmdq_get(c
);
805 if (queue
->item
== NULL
)
807 if (queue
->item
->flags
& CMDQ_WAITING
)
809 return (queue
->item
);
812 /* Print a guard line. */
814 cmdq_guard(struct cmdq_item
*item
, const char *guard
, int flags
)
816 struct client
*c
= item
->client
;
818 u_int number
= item
->number
;
820 if (c
!= NULL
&& (c
->flags
& CLIENT_CONTROL
))
821 control_write(c
, "%%%s %ld %u %d", guard
, t
, number
, flags
);
824 /* Show message from command. */
826 cmdq_print_data(struct cmdq_item
*item
, int parse
, struct evbuffer
*evb
)
828 server_client_print(item
->client
, parse
, evb
);
831 /* Show message from command. */
833 cmdq_print(struct cmdq_item
*item
, const char *fmt
, ...)
836 struct evbuffer
*evb
;
838 evb
= evbuffer_new();
840 fatalx("out of memory");
843 evbuffer_add_vprintf(evb
, fmt
, ap
);
846 cmdq_print_data(item
, 0, evb
);
850 /* Show error from command. */
852 cmdq_error(struct cmdq_item
*item
, const char *fmt
, ...)
854 struct client
*c
= item
->client
;
855 struct cmd
*cmd
= item
->cmd
;
862 xvasprintf(&msg
, fmt
, ap
);
865 log_debug("%s: %s", __func__
, msg
);
868 cmd_get_source(cmd
, &file
, &line
);
869 cfg_add_cause("%s:%u: %s", file
, line
, msg
);
870 } else if (c
->session
== NULL
|| (c
->flags
& CLIENT_CONTROL
)) {
871 server_add_message("%s message: %s", c
->name
, msg
);
872 if (~c
->flags
& CLIENT_UTF8
) {
874 msg
= utf8_sanitize(tmp
);
877 if (c
->flags
& CLIENT_CONTROL
)
878 control_write(c
, "%s", msg
);
880 file_error(c
, "%s\n", msg
);
883 *msg
= toupper((u_char
) *msg
);
884 status_message_set(c
, -1, 1, 0, "%s", msg
);