Bump liberty, use newer configuration format

So that we don't need to maintain our own string parser for attribute strings.

More or less just plugging in what the old code has evolved into in degesch.
This commit is contained in:
Přemysl Eric Janouch 2015-12-13 22:31:06 +01:00
parent 50c8ef12ac
commit 3e2728443b
2 changed files with 233 additions and 208 deletions

View File

@ -21,18 +21,27 @@
/// Some arbitrary limit for the history file /// Some arbitrary limit for the history file
#define HISTORY_LIMIT 10000 #define HISTORY_LIMIT 10000
// String constants for all attributes we use for output // A table of all attributes we use for output
#define ATTR_PROMPT "attr_prompt" #define ATTR_TABLE(XX) \
#define ATTR_RESET "attr_reset" XX( PROMPT, "prompt", "Terminal attrs for the prompt" ) \
#define ATTR_WARNING "attr_warning" XX( RESET, "reset", "String to reset terminal attributes" ) \
#define ATTR_ERROR "attr_error" XX( WARNING, "warning", "Terminal attrs for warnings" ) \
#define ATTR_INCOMING "attr_incoming" XX( ERROR, "error", "Terminal attrs for errors" ) \
#define ATTR_OUTGOING "attr_outgoing" XX( INCOMING, "incoming", "Terminal attrs for incoming traffic" ) \
XX( OUTGOING, "outgoing", "Terminal attrs for outgoing traffic" )
enum
{
#define XX(x, y, z) ATTR_ ## x,
ATTR_TABLE (XX)
#undef XX
ATTR_COUNT
};
// User data for logger functions to enable formatted logging // User data for logger functions to enable formatted logging
#define print_fatal_data ATTR_ERROR #define print_fatal_data ((void *) ATTR_ERROR)
#define print_error_data ATTR_ERROR #define print_error_data ((void *) ATTR_ERROR)
#define print_warning_data ATTR_WARNING #define print_warning_data ((void *) ATTR_WARNING)
#define LIBERTY_WANT_SSL #define LIBERTY_WANT_SSL
#define LIBERTY_WANT_PROTO_HTTP #define LIBERTY_WANT_PROTO_HTTP
@ -63,23 +72,6 @@
return false; \ return false; \
BLOCK_END BLOCK_END
// --- Configuration (application-specific) ------------------------------------
static struct simple_config_item g_config_table[] =
{
{ ATTR_PROMPT, NULL, "Terminal attributes for the prompt" },
{ ATTR_RESET, NULL, "String to reset terminal attributes" },
{ ATTR_WARNING, NULL, "Terminal attributes for warnings" },
{ ATTR_ERROR, NULL, "Terminal attributes for errors" },
{ ATTR_INCOMING, NULL, "Terminal attributes for incoming traffic" },
{ ATTR_OUTGOING, NULL, "Terminal attributes for outgoing traffic" },
{ "ca_file", NULL, "OpenSSL trusted CA certificates file" },
{ "ca_path", NULL, "OpenSSL trusted CA certificates path" },
{ NULL, NULL, NULL }
};
// --- Terminal ---------------------------------------------------------------- // --- Terminal ----------------------------------------------------------------
static struct static struct
@ -233,6 +225,8 @@ ws_context_init (struct ws_context *self)
str_vector_init (&self->extra_headers); str_vector_init (&self->extra_headers);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct curl_context struct curl_context
{ {
CURL *curl; ///< cURL handle CURL *curl; ///< cURL handle
@ -262,7 +256,10 @@ static struct app_context
struct ws_context ws; ///< WebSockets backend data struct ws_context ws; ///< WebSockets backend data
struct curl_context curl; ///< cURL backend data struct curl_context curl; ///< cURL backend data
struct str_map config; ///< Program configuration char *attrs_defaults[ATTR_COUNT]; ///< Default terminal attributes
char *attrs[ATTR_COUNT]; ///< Terminal attributes
struct config config; ///< Program configuration
enum color_mode color_mode; ///< Colour output mode enum color_mode color_mode; ///< Colour output mode
bool pretty_print; ///< Whether to pretty print bool pretty_print; ///< Whether to pretty print
bool verbose; ///< Print requests bool verbose; ///< Print requests
@ -279,6 +276,146 @@ static struct app_context
} }
g_ctx; g_ctx;
// --- Configuration -----------------------------------------------------------
static void on_config_attribute_change (struct config_item *item);
static struct config_schema g_config_connection[] =
{
{ .name = "tls_ca_file",
.comment = "OpenSSL CA bundle file",
.type = CONFIG_ITEM_STRING },
{ .name = "tls_ca_path",
.comment = "OpenSSL CA bundle path",
.type = CONFIG_ITEM_STRING },
{}
};
static struct config_schema g_config_attributes[] =
{
#define XX(x, y, z) { .name = y, .comment = z, .type = CONFIG_ITEM_STRING, \
.on_change = on_config_attribute_change },
ATTR_TABLE (XX)
#undef XX
{}
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
load_config_connection (struct config_item *subtree, void *user_data)
{
config_schema_apply_to_object (g_config_connection, subtree, user_data);
}
static void
load_config_attributes (struct config_item *subtree, void *user_data)
{
config_schema_apply_to_object (g_config_attributes, subtree, user_data);
}
static void
register_config_modules (struct app_context *ctx)
{
struct config *config = &ctx->config;
config_register_module (config, "connection", load_config_connection, ctx);
config_register_module (config, "attributes", load_config_attributes, ctx);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const char *
get_config_string (struct config_item *root, const char *key)
{
struct config_item *item = config_item_get (root, key, NULL);
hard_assert (item);
if (item->type == CONFIG_ITEM_NULL)
return NULL;
hard_assert (config_item_type_is_string (item->type));
return item->value.string.str;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
save_configuration (struct config_item *root, const char *path_hint)
{
struct str data;
str_init (&data);
str_append (&data,
"# " PROGRAM_NAME " " PROGRAM_VERSION " configuration file\n"
"#\n"
"# Relative paths are searched for in ${XDG_CONFIG_HOME:-~/.config}\n"
"# /" PROGRAM_NAME " as well as in $XDG_CONFIG_DIRS/" PROGRAM_NAME "\n"
"#\n"
"# All text must be in UTF-8.\n"
"\n");
config_item_write (root, true, &data);
struct error *e = NULL;
char *filename = write_configuration_file (path_hint, &data, &e);
str_free (&data);
if (!filename)
{
print_error ("%s: %s", "saving configuration failed", e->message);
error_free (e);
}
else
print_status ("configuration written to `%s'", filename);
free (filename);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// TODO: consider moving to liberty, degesch has exactly the same function
static struct config_item *
load_configuration_file (const char *filename, struct error **e)
{
struct config_item *root = NULL;
struct str data;
str_init (&data);
if (!read_file (filename, &data, e))
goto end;
struct error *error = NULL;
if (!(root = config_item_parse (data.str, data.len, false, &error)))
{
error_set (e, "parse error: %s", error->message);
error_free (error);
}
end:
str_free (&data);
return root;
}
static void
load_configuration (struct app_context *ctx)
{
char *filename = resolve_filename
(PROGRAM_NAME ".conf", resolve_relative_config_filename);
if (!filename)
return;
struct error *e = NULL;
struct config_item *root = load_configuration_file (filename, &e);
free (filename);
if (e)
{
print_error ("error loading configuration: %s", e->message);
error_free (e);
exit (EXIT_FAILURE);
}
if (root)
{
config_load (&ctx->config, root);
config_schema_call_changed (ctx->config.root);
}
}
// --- Attributed output ------------------------------------------------------- // --- Attributed output -------------------------------------------------------
typedef int (*terminal_printer_fn) (int); typedef int (*terminal_printer_fn) (int);
@ -301,30 +438,24 @@ get_attribute_printer (FILE *stream)
static void static void
vprint_attributed (struct app_context *ctx, vprint_attributed (struct app_context *ctx,
FILE *stream, const char *attribute, const char *fmt, va_list ap) FILE *stream, intptr_t attribute, const char *fmt, va_list ap)
{ {
terminal_printer_fn printer = get_attribute_printer (stream); terminal_printer_fn printer = get_attribute_printer (stream);
if (!attribute) if (!attribute)
printer = NULL; printer = NULL;
if (printer) if (printer)
{ tputs (ctx->attrs[attribute], 1, printer);
const char *value = str_map_find (&ctx->config, attribute);
tputs (value, 1, printer);
}
vfprintf (stream, fmt, ap); vfprintf (stream, fmt, ap);
if (printer) if (printer)
{ tputs (ctx->attrs[ATTR_RESET], 1, printer);
const char *value = str_map_find (&ctx->config, ATTR_RESET);
tputs (value, 1, printer);
}
} }
static void static void
print_attributed (struct app_context *ctx, print_attributed (struct app_context *ctx,
FILE *stream, const char *attribute, const char *fmt, ...) FILE *stream, intptr_t attribute, const char *fmt, ...)
{ {
va_list ap; va_list ap;
va_start (ap, fmt); va_start (ap, fmt);
@ -353,8 +484,8 @@ log_message_attributed (void *user_data, const char *quote, const char *fmt,
rl_redisplay (); rl_redisplay ();
} }
print_attributed (&g_ctx, stream, user_data, "%s", quote); print_attributed (&g_ctx, stream, (intptr_t) user_data, "%s", quote);
vprint_attributed (&g_ctx, stream, user_data, fmt, ap); vprint_attributed (&g_ctx, stream, (intptr_t) user_data, fmt, ap);
fputs ("\n", stream); fputs ("\n", stream);
if (g_ctx.readline_prompt_shown) if (g_ctx.readline_prompt_shown)
@ -370,36 +501,31 @@ log_message_attributed (void *user_data, const char *quote, const char *fmt,
static void static void
init_colors (struct app_context *ctx) init_colors (struct app_context *ctx)
{ {
char **defaults = ctx->attrs_defaults;
#define INIT_ATTR(id, ti) defaults[ATTR_ ## id] = xstrdup ((ti))
// Use escape sequences from terminfo if possible, and SGR as a fallback // Use escape sequences from terminfo if possible, and SGR as a fallback
if (init_terminal ()) if (init_terminal ())
{ {
const char *attrs[][2] = INIT_ATTR (PROMPT, enter_bold_mode);
{ INIT_ATTR (RESET, exit_attribute_mode);
{ ATTR_PROMPT, enter_bold_mode }, INIT_ATTR (WARNING, g_terminal.color_set[COLOR_YELLOW]);
{ ATTR_RESET, exit_attribute_mode }, INIT_ATTR (ERROR, g_terminal.color_set[COLOR_RED]);
{ ATTR_WARNING, g_terminal.color_set[3] }, INIT_ATTR (INCOMING, "");
{ ATTR_ERROR, g_terminal.color_set[1] }, INIT_ATTR (OUTGOING, "");
{ ATTR_INCOMING, "" },
{ ATTR_OUTGOING, "" },
};
for (size_t i = 0; i < N_ELEMENTS (attrs); i++)
str_map_set (&ctx->config, attrs[i][0], xstrdup (attrs[i][1]));
} }
else else
{ {
const char *attrs[][2] = INIT_ATTR (PROMPT, "\x1b[1m");
{ INIT_ATTR (RESET, "\x1b[0m");
{ ATTR_PROMPT, "\x1b[1m" }, INIT_ATTR (WARNING, "\x1b[33m");
{ ATTR_RESET, "\x1b[0m" }, INIT_ATTR (ERROR, "\x1b[31m");
{ ATTR_WARNING, "\x1b[33m" }, INIT_ATTR (INCOMING, "");
{ ATTR_ERROR, "\x1b[31m" }, INIT_ATTR (OUTGOING, "");
{ ATTR_INCOMING, "" },
{ ATTR_OUTGOING, "" },
};
for (size_t i = 0; i < N_ELEMENTS (attrs); i++)
str_map_set (&ctx->config, attrs[i][0], xstrdup (attrs[i][1]));
} }
#undef INIT_ATTR
switch (ctx->color_mode) switch (ctx->color_mode)
{ {
case COLOR_ALWAYS: case COLOR_ALWAYS:
@ -416,150 +542,42 @@ init_colors (struct app_context *ctx)
} }
g_log_message_real = log_message_attributed; g_log_message_real = log_message_attributed;
// Apply the default values so that we start with any formatting at all
config_schema_call_changed
(config_item_get (ctx->config.root, "attributes", NULL));
} }
// --- Configuration loading --------------------------------------------------- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static bool static ssize_t
read_hexa_escape (const char **cursor, struct str *output) attr_by_name (const char *name)
{ {
int i; static const char *table[ATTR_COUNT] =
char c, code = 0;
for (i = 0; i < 2; i++)
{ {
c = tolower (*(*cursor)); #define XX(x, y, z) [ATTR_ ## x] = y,
if (c >= '0' && c <= '9') ATTR_TABLE (XX)
code = (code << 4) | (c - '0'); #undef XX
else if (c >= 'a' && c <= 'f') };
code = (code << 4) | (c - 'a' + 10);
else
break;
(*cursor)++; for (size_t i = 0; i < N_ELEMENTS (table); i++)
} if (!strcmp (name, table[i]))
return i;
if (!i) return -1;
return false;
str_append_c (output, code);
return true;
}
static bool
read_octal_escape (const char **cursor, struct str *output)
{
int i;
char c, code = 0;
for (i = 0; i < 3; i++)
{
c = *(*cursor);
if (c < '0' || c > '7')
break;
code = (code << 3) | (c - '0');
(*cursor)++;
}
if (!i)
return false;
str_append_c (output, code);
return true;
}
static bool
read_string_escape_sequence (const char **cursor,
struct str *output, struct error **e)
{
int c;
switch ((c = *(*cursor)++))
{
case '?': str_append_c (output, '?'); break;
case '"': str_append_c (output, '"'); break;
case '\\': str_append_c (output, '\\'); break;
case 'a': str_append_c (output, '\a'); break;
case 'b': str_append_c (output, '\b'); break;
case 'f': str_append_c (output, '\f'); break;
case 'n': str_append_c (output, '\n'); break;
case 'r': str_append_c (output, '\r'); break;
case 't': str_append_c (output, '\t'); break;
case 'v': str_append_c (output, '\v'); break;
case 'e':
case 'E':
str_append_c (output, '\x1b');
break;
case 'x':
case 'X':
if (!read_hexa_escape (cursor, output))
FAIL ("invalid hexadecimal escape");
break;
case '\0':
FAIL ("premature end of escape sequence");
default:
(*cursor)--;
if (!read_octal_escape (cursor, output))
FAIL ("unknown escape sequence");
}
return true;
}
static bool
unescape_string (const char *s, struct str *output, struct error **e)
{
int c;
while ((c = *s++))
{
if (c != '\\')
str_append_c (output, c);
else if (!read_string_escape_sequence (&s, output, e))
return false;
}
return true;
} }
static void static void
load_config (struct app_context *ctx) on_config_attribute_change (struct config_item *item)
{ {
// TODO: employ a better configuration file format, so that we don't have struct app_context *ctx = item->user_data;
// to do this convoluted post-processing anymore. ssize_t id = attr_by_name (item->schema->name);
if (id != -1)
struct str_map map;
str_map_init (&map);
map.free = free;
struct error *e = NULL;
if (!simple_config_update_from_file (&map, &e))
{ {
print_error ("error loading configuration: %s", e->message); free (ctx->attrs[id]);
error_free (e); ctx->attrs[id] = xstrdup (item->type == CONFIG_ITEM_NULL
exit (EXIT_FAILURE); ? ctx->attrs_defaults[id]
: item->value.string.str);
} }
struct str_map_iter iter;
str_map_iter_init (&iter, &map);
while (str_map_iter_next (&iter))
{
struct error *e = NULL;
struct str value;
str_init (&value);
if (!unescape_string (iter.link->data, &value, &e))
{
print_error ("error reading configuration: %s: %s",
iter.link->key, e->message);
error_free (e);
exit (EXIT_FAILURE);
}
str_map_set (&ctx->config, iter.link->key, str_steal (&value));
}
str_map_free (&map);
} }
// --- WebSockets backend ------------------------------------------------------ // --- WebSockets backend ------------------------------------------------------
@ -995,8 +1013,11 @@ backend_ws_set_up_ssl_ctx (struct app_context *ctx)
return true; return true;
} }
const char *ca_file = str_map_find (&ctx->config, "ca_file"); // TODO: try to resolve filenames relative to configuration directories
const char *ca_path = str_map_find (&ctx->config, "ca_path"); const char *ca_file = get_config_string
(ctx->config.root, "connection.tls_ca_file");
const char *ca_path = get_config_string
(ctx->config.root, "connection.tls_ca_path");
if (ca_file || ca_path) if (ca_file || ca_path)
{ {
if (SSL_CTX_load_verify_locations (self->ssl_ctx, ca_file, ca_path)) if (SSL_CTX_load_verify_locations (self->ssl_ctx, ca_file, ca_path))
@ -1567,8 +1588,11 @@ backend_curl_init (struct app_context *ctx,
if (!ctx->trust_all) if (!ctx->trust_all)
{ {
const char *ca_file = str_map_find (&ctx->config, "ca_file"); // TODO: try to resolve filenames relative to configuration directories
const char *ca_path = str_map_find (&ctx->config, "ca_path"); const char *ca_file = get_config_string
(ctx->config.root, "connection.tls_ca_file");
const char *ca_path = get_config_string
(ctx->config.root, "connection.tls_ca_path");
if ((ca_file && curl_easy_setopt (curl, CURLOPT_CAINFO, ca_file)) if ((ca_file && curl_easy_setopt (curl, CURLOPT_CAINFO, ca_file))
|| (ca_path && curl_easy_setopt (curl, CURLOPT_CAPATH, ca_path))) || (ca_path && curl_easy_setopt (curl, CURLOPT_CAPATH, ca_path)))
exit_fatal ("cURL setup failed"); exit_fatal ("cURL setup failed");
@ -2040,7 +2064,7 @@ parse_program_arguments (struct app_context *ctx, int argc, char **argv,
} }
break; break;
case 'w': case 'w':
call_simple_config_write_default (optarg, g_config_table); save_configuration (ctx->config.root, optarg);
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
default: default:
@ -2065,15 +2089,16 @@ parse_program_arguments (struct app_context *ctx, int argc, char **argv,
int int
main (int argc, char *argv[]) main (int argc, char *argv[])
{ {
str_map_init (&g_ctx.config); config_init (&g_ctx.config);
g_ctx.config.free = free; register_config_modules (&g_ctx);
config_load (&g_ctx.config, config_item_object ());
char *origin = NULL; char *origin = NULL;
char *endpoint = NULL; char *endpoint = NULL;
parse_program_arguments (&g_ctx, argc, argv, &origin, &endpoint); parse_program_arguments (&g_ctx, argc, argv, &origin, &endpoint);
init_colors (&g_ctx); init_colors (&g_ctx);
load_config (&g_ctx); load_configuration (&g_ctx);
struct http_parser_url url; struct http_parser_url url;
if (http_parser_parse_url (endpoint, strlen (endpoint), false, &url)) if (http_parser_parse_url (endpoint, strlen (endpoint), false, &url))
@ -2145,11 +2170,11 @@ main (int argc, char *argv[])
else else
{ {
// XXX: to be completely correct, we should use tputs, but we cannot // XXX: to be completely correct, we should use tputs, but we cannot
const char *prompt_attrs = str_map_find (&g_ctx.config, ATTR_PROMPT);
const char *reset_attrs = str_map_find (&g_ctx.config, ATTR_RESET);
g_ctx.readline_prompt = xstrdup_printf ("%c%s%cjson-rpc> %c%s%c", g_ctx.readline_prompt = xstrdup_printf ("%c%s%cjson-rpc> %c%s%c",
RL_PROMPT_START_IGNORE, prompt_attrs, RL_PROMPT_END_IGNORE, RL_PROMPT_START_IGNORE, g_ctx.attrs[ATTR_PROMPT],
RL_PROMPT_START_IGNORE, reset_attrs, RL_PROMPT_END_IGNORE); RL_PROMPT_END_IGNORE,
RL_PROMPT_START_IGNORE, g_ctx.attrs[ATTR_RESET],
RL_PROMPT_END_IGNORE);
} }
// So that if the remote end closes the connection, attempts to write to // So that if the remote end closes the connection, attempts to write to
@ -2204,7 +2229,7 @@ main (int argc, char *argv[])
g_ctx.backend->destroy (&g_ctx); g_ctx.backend->destroy (&g_ctx);
free (origin); free (origin);
free (g_ctx.readline_prompt); free (g_ctx.readline_prompt);
str_map_free (&g_ctx.config); config_free (&g_ctx.config);
free_terminal (); free_terminal ();
ev_loop_destroy (loop); ev_loop_destroy (loop);
return EXIT_SUCCESS; return EXIT_SUCCESS;

@ -1 +1 @@
Subproject commit 91fca5cb054fc95fe2e7ac090cac2c3a302565ca Subproject commit 8b2e41ed8ffac0494763495896c6a80a9e9db543