From 4928f9ed62b62c3704bce093369ee476268b435f Mon Sep 17 00:00:00 2001 From: Přemysl Janouch Date: Sun, 3 May 2015 16:47:16 +0200 Subject: degesch: add a read-only /set command --- degesch.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/degesch.c b/degesch.c index 55fb325..b0a3e04 100644 --- a/degesch.c +++ b/degesch.c @@ -3547,6 +3547,106 @@ log_outcoming_notice (struct server *s, send_autosplit_message ((s), (struct send_autosplit_args) \ { "NOTICE", (target), (message), log_outcoming_notice, "", "" }) +// --- Configuration dumper ---------------------------------------------------- + +struct config_dump_level +{ + struct config_dump_level *next; ///< Next print level + const char *name; ///< Name of the object +}; + +struct config_dump_data +{ + struct config_dump_level *head; ///< The first level + struct config_dump_level **tail; ///< Where to place further levels + struct str_vector *output; ///< Where to place new entries +}; + +static void config_dump_item + (struct config_item_ *item, struct config_dump_data *data); + +static void +config_dump_children + (struct config_item_ *object, struct config_dump_data *data) +{ + hard_assert (object->type = CONFIG_ITEM_OBJECT); + + struct config_dump_level level; + level.next = NULL; + + struct config_dump_level **prev_tail = data->tail; + *data->tail = &level; + data->tail = &level.next; + + struct str_map_iter iter; + str_map_iter_init (&iter, &object->value.object); + struct config_item_ *child; + while ((child = str_map_iter_next (&iter))) + { + level.name = iter.link->key; + config_dump_item (child, data); + } + + data->tail = prev_tail; + *data->tail = NULL; +} + +static void +config_dump_item (struct config_item_ *item, struct config_dump_data *data) +{ + struct str line; + str_init (&line); + + struct config_dump_level *iter = data->head; + if (iter) + { + str_append (&line, iter->name); + iter = iter->next; + } + for (; iter; iter = iter->next) + str_append_printf (&line, ".%s", iter->name); + + // Empty objects will show as such + if (item->type == CONFIG_ITEM_OBJECT + && item->value.object.len) + { + config_dump_children (item, data); + return; + } + + // Don't bother writing out null values everywhere + struct config_schema *schema = item->schema; + bool has_default = schema && schema->default_; + if (item->type != CONFIG_ITEM_NULL || has_default) + { + str_append (&line, " = "); + + struct str value; + str_init (&value); + config_item_write (item, false, &value); + str_append_str (&line, &value); + + if (has_default && strcmp (schema->default_, value.str)) + str_append_printf (&line, " (default: %s)", schema->default_); + else if (!has_default && item->type != CONFIG_ITEM_NULL) + str_append_printf (&line, " (default: %s)", "null"); + str_free (&value); + } + + str_vector_add_owned (data->output, str_steal (&line)); +} + +static void +config_dump (struct config_item_ *root, struct str_vector *output) +{ + struct config_dump_data data; + data.head = NULL; + data.tail = &data.head; + data.output = output; + + config_dump_item (root, &data); +} + // --- User input handling ----------------------------------------------------- static bool handle_command_help (struct app_context *, char *); @@ -3673,6 +3773,80 @@ handle_command_buffer (struct app_context *ctx, char *arguments) return true; } +static int +str_vector_sort_cb (const void *a, const void *b) +{ + return strcmp (*(const char **) a, *(const char **) b); +} + +static void +str_vector_sort (struct str_vector *self) +{ + qsort (self->vector, self->len, sizeof *self->vector, str_vector_sort_cb); +} + +static bool +handle_command_set (struct app_context *ctx, char *arguments) +{ + struct str_vector all; + str_vector_init (&all); + config_dump (ctx->config.root, &all); + str_vector_sort (&all); + + char *option = "*"; + if (*arguments) + option = cut_word (&arguments); + + // Filter out results by wildcard matching + for (size_t i = 0; i < all.len; i++) + { + char *key = xstrdup (all.vector[i]); + char *end = strchr (key, ' '); + if (end) + *end = '\0'; + if (fnmatch (option, key, 0)) + str_vector_remove (&all, i--); + free (key); + } + + if (!*arguments) + { + buffer_send_status (ctx, ctx->global_buffer, "%s", ""); + for (size_t i = 0; i < all.len; i++) + buffer_send_status (ctx, ctx->global_buffer, "%s", all.vector[i]); + str_vector_free (&all); + return true; + } + + char *op = cut_word (&arguments); + bool add = false; + bool remove = false; + + if (!strcmp (op, "+=")) add = true; + else if (!strcmp (op, "-=")) remove = true; + else if (strcmp (op, "=")) return false; + + if (!arguments) + return false; + + char *value = cut_word (&arguments); + struct error *e = NULL; + struct config_item_ *new_ = + config_item_parse (value, strlen (value), true, &e); + if (e) + { + buffer_send_error (ctx, ctx->global_buffer, + "Invalid value: %s", e->message); + error_free (e); + return true; + } + + // TODO: try to set the value, or modify the string list + buffer_send_error (ctx, ctx->global_buffer, "Not implemented"); + config_item_destroy (new_); + return true; +} + static bool handle_command_msg (struct app_context *ctx, char *arguments) { @@ -3901,6 +4075,8 @@ g_command_handlers[] = "[]" }, { "buffer", handle_command_buffer, "Manage buffers", "list | clear | move | { close [ | ] } | " }, + { "set", handle_command_set, "Manage configuration", + "[