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
)
242 return (cmdq_new_state(&state
->current
, &state
->event
, state
->flags
));
247 cmdq_free_state(struct cmdq_state
*state
)
249 if (--state
->references
!= 0)
252 if (state
->formats
!= NULL
)
253 format_free(state
->formats
);
257 /* Add a format to command queue. */
259 cmdq_add_format(struct cmdq_state
*state
, const char *key
, const char *fmt
, ...)
265 xvasprintf(&value
, fmt
, ap
);
268 if (state
->formats
== NULL
)
269 state
->formats
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
270 format_add(state
->formats
, key
, "%s", value
);
275 /* Add formats to command queue. */
277 cmdq_add_formats(struct cmdq_state
*state
, struct format_tree
*ft
)
279 if (state
->formats
== NULL
)
280 state
->formats
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
281 format_merge(state
->formats
, ft
);
284 /* Merge formats from item. */
286 cmdq_merge_formats(struct cmdq_item
*item
, struct format_tree
*ft
)
288 const struct cmd_entry
*entry
;
290 if (item
->cmd
!= NULL
) {
291 entry
= cmd_get_entry(item
->cmd
);
292 format_add(ft
, "command", "%s", entry
->name
);
294 if (item
->state
->formats
!= NULL
)
295 format_merge(ft
, item
->state
->formats
);
298 /* Append an item. */
300 cmdq_append(struct client
*c
, struct cmdq_item
*item
)
302 struct cmdq_list
*queue
= cmdq_get(c
);
303 struct cmdq_item
*next
;
314 TAILQ_INSERT_TAIL(&queue
->list
, item
, entry
);
315 log_debug("%s %s: %s", __func__
, cmdq_name(c
), item
->name
);
318 } while (item
!= NULL
);
319 return (TAILQ_LAST(&queue
->list
, cmdq_item_list
));
322 /* Insert an item. */
324 cmdq_insert_after(struct cmdq_item
*after
, struct cmdq_item
*item
)
326 struct client
*c
= after
->client
;
327 struct cmdq_list
*queue
= after
->queue
;
328 struct cmdq_item
*next
;
332 item
->next
= after
->next
;
340 TAILQ_INSERT_AFTER(&queue
->list
, after
, item
, entry
);
341 log_debug("%s %s: %s after %s", __func__
, cmdq_name(c
),
342 item
->name
, after
->name
);
346 } while (item
!= NULL
);
352 cmdq_insert_hook(struct session
*s
, struct cmdq_item
*item
,
353 struct cmd_find_state
*current
, const char *fmt
, ...)
355 struct cmdq_state
*state
= item
->state
;
356 struct cmd
*cmd
= item
->cmd
;
357 struct args
*args
= cmd_get_args(cmd
);
358 struct args_entry
*ae
;
359 struct args_value
*av
;
362 char *name
, tmp
[32], flag
, *arguments
;
365 struct cmdq_item
*new_item
;
366 struct cmdq_state
*new_state
;
367 struct options_entry
*o
;
368 struct options_array_item
*a
;
369 struct cmd_list
*cmdlist
;
371 if (item
->state
->flags
& CMDQ_STATE_NOHOOKS
)
374 oo
= global_s_options
;
379 xvasprintf(&name
, fmt
, ap
);
382 o
= options_get(oo
, name
);
387 log_debug("running hook %s (parent %p)", name
, item
);
390 * The hooks get a new state because they should not update the current
391 * target or formats for any subsequent commands.
393 new_state
= cmdq_new_state(current
, &state
->event
, CMDQ_STATE_NOHOOKS
);
394 cmdq_add_format(new_state
, "hook", "%s", name
);
396 arguments
= args_print(args
);
397 cmdq_add_format(new_state
, "hook_arguments", "%s", arguments
);
400 for (i
= 0; i
< args_count(args
); i
++) {
401 xsnprintf(tmp
, sizeof tmp
, "hook_argument_%d", i
);
402 cmdq_add_format(new_state
, tmp
, "%s", args_string(args
, i
));
404 flag
= args_first(args
, &ae
);
406 value
= args_get(args
, flag
);
408 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c", flag
);
409 cmdq_add_format(new_state
, tmp
, "1");
411 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c", flag
);
412 cmdq_add_format(new_state
, tmp
, "%s", value
);
416 av
= args_first_value(args
, flag
);
418 xsnprintf(tmp
, sizeof tmp
, "hook_flag_%c_%d", flag
, i
);
419 cmdq_add_format(new_state
, tmp
, "%s", av
->string
);
421 av
= args_next_value(av
);
424 flag
= args_next(&ae
);
427 a
= options_array_first(o
);
429 cmdlist
= options_array_item_value(a
)->cmdlist
;
430 if (cmdlist
!= NULL
) {
431 new_item
= cmdq_get_command(cmdlist
, new_state
);
433 item
= cmdq_insert_after(item
, new_item
);
435 item
= cmdq_append(NULL
, new_item
);
437 a
= options_array_next(a
);
440 cmdq_free_state(new_state
);
444 /* Continue processing command queue. */
446 cmdq_continue(struct cmdq_item
*item
)
448 item
->flags
&= ~CMDQ_WAITING
;
451 /* Remove an item. */
453 cmdq_remove(struct cmdq_item
*item
)
455 if (item
->client
!= NULL
)
456 server_client_unref(item
->client
);
457 if (item
->cmdlist
!= NULL
)
458 cmd_list_free(item
->cmdlist
);
459 cmdq_free_state(item
->state
);
461 TAILQ_REMOVE(&item
->queue
->list
, item
, entry
);
467 /* Remove all subsequent items that match this item's group. */
469 cmdq_remove_group(struct cmdq_item
*item
)
471 struct cmdq_item
*this, *next
;
473 if (item
->group
== 0)
475 this = TAILQ_NEXT(item
, entry
);
476 while (this != NULL
) {
477 next
= TAILQ_NEXT(this, entry
);
478 if (this->group
== item
->group
)
484 /* Empty command callback. */
485 static enum cmd_retval
486 cmdq_empty_command(__unused
struct cmdq_item
*item
, __unused
void *data
)
488 return (CMD_RETURN_NORMAL
);
491 /* Get a command for the command queue. */
493 cmdq_get_command(struct cmd_list
*cmdlist
, struct cmdq_state
*state
)
495 struct cmdq_item
*item
, *first
= NULL
, *last
= NULL
;
497 const struct cmd_entry
*entry
;
500 if ((cmd
= cmd_list_first(cmdlist
)) == NULL
)
501 return (cmdq_get_callback(cmdq_empty_command
, NULL
));
504 state
= cmdq_new_state(NULL
, NULL
, 0);
508 while (cmd
!= NULL
) {
509 entry
= cmd_get_entry(cmd
);
511 item
= xcalloc(1, sizeof *item
);
512 xasprintf(&item
->name
, "[%s/%p]", entry
->name
, item
);
513 item
->type
= CMDQ_COMMAND
;
515 item
->group
= cmd_get_group(cmd
);
516 item
->state
= cmdq_link_state(state
);
518 item
->cmdlist
= cmdlist
;
521 cmdlist
->references
++;
522 log_debug("%s: %s group %u", __func__
, item
->name
, item
->group
);
530 cmd
= cmd_list_next(cmd
);
534 cmdq_free_state(state
);
538 /* Fill in flag for a command. */
539 static enum cmd_retval
540 cmdq_find_flag(struct cmdq_item
*item
, struct cmd_find_state
*fs
,
541 const struct cmd_entry_flag
*flag
)
545 if (flag
->flag
== 0) {
546 cmd_find_from_client(fs
, item
->target_client
, 0);
547 return (CMD_RETURN_NORMAL
);
550 value
= args_get(cmd_get_args(item
->cmd
), flag
->flag
);
551 if (cmd_find_target(fs
, item
, value
, flag
->type
, flag
->flags
) != 0) {
552 cmd_find_clear_state(fs
, 0);
553 return (CMD_RETURN_ERROR
);
555 return (CMD_RETURN_NORMAL
);
558 /* Add message with command. */
560 cmdq_add_message(struct cmdq_item
*item
)
562 struct client
*c
= item
->client
;
563 struct cmdq_state
*state
= item
->state
;
570 tmp
= cmd_print(item
->cmd
);
572 uid
= proc_get_peer_uid(c
->peer
);
573 if (uid
!= (uid_t
)-1 && uid
!= getuid()) {
574 if ((pw
= getpwuid(uid
)) != NULL
)
575 xasprintf(&user
, "[%s]", pw
->pw_name
);
577 user
= xstrdup("[unknown]");
580 if (c
->session
!= NULL
&& state
->event
.key
!= KEYC_NONE
) {
581 key
= key_string_lookup_key(state
->event
.key
, 0);
582 server_add_message("%s%s key %s: %s", c
->name
, user
,
585 server_add_message("%s%s command: %s", c
->name
, user
,
590 server_add_message("command: %s", tmp
);
594 /* Fire command on command queue. */
595 static enum cmd_retval
596 cmdq_fire_command(struct cmdq_item
*item
)
598 const char *name
= cmdq_name(item
->client
);
599 struct cmdq_state
*state
= item
->state
;
600 struct cmd
*cmd
= item
->cmd
;
601 struct args
*args
= cmd_get_args(cmd
);
602 const struct cmd_entry
*entry
= cmd_get_entry(cmd
);
603 struct client
*tc
, *saved
= item
->client
;
604 enum cmd_retval retval
;
605 struct cmd_find_state
*fsp
, fs
;
606 int flags
, quiet
= 0;
610 cmdq_add_message(item
);
611 if (log_get_level() > 1) {
612 tmp
= cmd_print(cmd
);
613 log_debug("%s %s: (%u) %s", __func__
, name
, item
->group
, tmp
);
617 flags
= !!(state
->flags
& CMDQ_STATE_CONTROL
);
618 cmdq_guard(item
, "begin", flags
);
620 if (item
->client
== NULL
)
621 item
->client
= cmd_find_client(item
, NULL
, 1);
623 if (entry
->flags
& CMD_CLIENT_CANFAIL
)
625 if (entry
->flags
& CMD_CLIENT_CFLAG
) {
626 tc
= cmd_find_client(item
, args_get(args
, 'c'), quiet
);
627 if (tc
== NULL
&& !quiet
) {
628 retval
= CMD_RETURN_ERROR
;
631 } else if (entry
->flags
& CMD_CLIENT_TFLAG
) {
632 tc
= cmd_find_client(item
, args_get(args
, 't'), quiet
);
633 if (tc
== NULL
&& !quiet
) {
634 retval
= CMD_RETURN_ERROR
;
638 tc
= cmd_find_client(item
, NULL
, 1);
639 item
->target_client
= tc
;
641 retval
= cmdq_find_flag(item
, &item
->source
, &entry
->source
);
642 if (retval
== CMD_RETURN_ERROR
)
644 retval
= cmdq_find_flag(item
, &item
->target
, &entry
->target
);
645 if (retval
== CMD_RETURN_ERROR
)
648 retval
= entry
->exec(cmd
, item
);
649 if (retval
== CMD_RETURN_ERROR
)
652 if (entry
->flags
& CMD_AFTERHOOK
) {
653 if (cmd_find_valid_state(&item
->target
))
655 else if (cmd_find_valid_state(&item
->state
->current
))
656 fsp
= &item
->state
->current
;
657 else if (cmd_find_from_client(&fs
, item
->client
, 0) == 0)
661 cmdq_insert_hook(fsp
->s
, item
, fsp
, "after-%s", entry
->name
);
665 item
->client
= saved
;
666 if (retval
== CMD_RETURN_ERROR
)
667 cmdq_guard(item
, "error", flags
);
669 cmdq_guard(item
, "end", flags
);
673 /* Get a callback for the command queue. */
675 cmdq_get_callback1(const char *name
, cmdq_cb cb
, void *data
)
677 struct cmdq_item
*item
;
679 item
= xcalloc(1, sizeof *item
);
680 xasprintf(&item
->name
, "[%s/%p]", name
, item
);
681 item
->type
= CMDQ_CALLBACK
;
684 item
->state
= cmdq_new_state(NULL
, NULL
, 0);
692 /* Generic error callback. */
693 static enum cmd_retval
694 cmdq_error_callback(struct cmdq_item
*item
, void *data
)
698 cmdq_error(item
, "%s", error
);
701 return (CMD_RETURN_NORMAL
);
704 /* Get an error callback for the command queue. */
706 cmdq_get_error(const char *error
)
708 return (cmdq_get_callback(cmdq_error_callback
, xstrdup(error
)));
711 /* Fire callback on callback queue. */
712 static enum cmd_retval
713 cmdq_fire_callback(struct cmdq_item
*item
)
715 return (item
->cb(item
, item
->data
));
718 /* Process next item on command queue. */
720 cmdq_next(struct client
*c
)
722 struct cmdq_list
*queue
= cmdq_get(c
);
723 const char *name
= cmdq_name(c
);
724 struct cmdq_item
*item
;
725 enum cmd_retval retval
;
729 if (TAILQ_EMPTY(&queue
->list
)) {
730 log_debug("%s %s: empty", __func__
, name
);
733 if (TAILQ_FIRST(&queue
->list
)->flags
& CMDQ_WAITING
) {
734 log_debug("%s %s: waiting", __func__
, name
);
738 log_debug("%s %s: enter", __func__
, name
);
740 item
= queue
->item
= TAILQ_FIRST(&queue
->list
);
743 log_debug("%s %s: %s (%d), flags %x", __func__
, name
,
744 item
->name
, item
->type
, item
->flags
);
747 * Any item with the waiting flag set waits until an external
748 * event clears the flag (for example, a job - look at
751 if (item
->flags
& CMDQ_WAITING
)
755 * Items are only fired once, once the fired flag is set, a
756 * waiting flag can only be cleared by an external event.
758 if (~item
->flags
& CMDQ_FIRED
) {
759 item
->time
= time(NULL
);
760 item
->number
= ++number
;
762 switch (item
->type
) {
764 retval
= cmdq_fire_command(item
);
767 * If a command returns an error, remove any
768 * subsequent commands in the same group.
770 if (retval
== CMD_RETURN_ERROR
)
771 cmdq_remove_group(item
);
774 retval
= cmdq_fire_callback(item
);
777 retval
= CMD_RETURN_ERROR
;
780 item
->flags
|= CMDQ_FIRED
;
782 if (retval
== CMD_RETURN_WAIT
) {
783 item
->flags
|= CMDQ_WAITING
;
792 log_debug("%s %s: exit (empty)", __func__
, name
);
796 log_debug("%s %s: exit (wait)", __func__
, name
);
800 /* Get running item if any. */
802 cmdq_running(struct client
*c
)
804 struct cmdq_list
*queue
= cmdq_get(c
);
806 if (queue
->item
== NULL
)
808 if (queue
->item
->flags
& CMDQ_WAITING
)
810 return (queue
->item
);
813 /* Print a guard line. */
815 cmdq_guard(struct cmdq_item
*item
, const char *guard
, int flags
)
817 struct client
*c
= item
->client
;
819 u_int number
= item
->number
;
821 if (c
!= NULL
&& (c
->flags
& CLIENT_CONTROL
))
822 control_write(c
, "%%%s %ld %u %d", guard
, t
, number
, flags
);
825 /* Show message from command. */
827 cmdq_print_data(struct cmdq_item
*item
, int parse
, struct evbuffer
*evb
)
829 server_client_print(item
->client
, parse
, evb
);
832 /* Show message from command. */
834 cmdq_print(struct cmdq_item
*item
, const char *fmt
, ...)
837 struct evbuffer
*evb
;
839 evb
= evbuffer_new();
841 fatalx("out of memory");
844 evbuffer_add_vprintf(evb
, fmt
, ap
);
847 cmdq_print_data(item
, 0, evb
);
851 /* Show error from command. */
853 cmdq_error(struct cmdq_item
*item
, const char *fmt
, ...)
855 struct client
*c
= item
->client
;
856 struct cmd
*cmd
= item
->cmd
;
863 xvasprintf(&msg
, fmt
, ap
);
866 log_debug("%s: %s", __func__
, msg
);
869 cmd_get_source(cmd
, &file
, &line
);
870 cfg_add_cause("%s:%u: %s", file
, line
, msg
);
871 } else if (c
->session
== NULL
|| (c
->flags
& CLIENT_CONTROL
)) {
872 server_add_message("%s message: %s", c
->name
, msg
);
873 if (~c
->flags
& CLIENT_UTF8
) {
875 msg
= utf8_sanitize(tmp
);
878 if (c
->flags
& CLIENT_CONTROL
)
879 control_write(c
, "%s", msg
);
881 file_error(c
, "%s\n", msg
);
884 *msg
= toupper((u_char
) *msg
);
885 status_message_set(c
, -1, 1, 0, "%s", msg
);