File: //usr/share/passenger/ngx_http_passenger_module/ConfigGeneral/ManifestGeneration.c
#include "ManifestGeneration.h"
#include "../Configuration.h"
#include "cxx_supportlib/Constants.h"
#include "cxx_supportlib/FileTools/PathManipCBindings.h"
static PsgJsonValue *
generate_config_manifest(ngx_conf_t *cf, passenger_loc_conf_t *toplevel_plcf) {
manifest_gen_ctx_t ctx;
PsgJsonValue *result;
init_config_manifest_generation(cf, &ctx);
generate_config_manifest_for_autogenerated_main_conf(&ctx, &passenger_main_conf);
recursively_generate_config_manifest_for_loc_conf(&ctx, toplevel_plcf);
reverse_manifest_value_hierarchies(&ctx);
set_manifest_autogenerated_global_conf_defaults(&ctx);
set_manifest_autogenerated_app_conf_defaults(&ctx,
ctx.default_app_config_container);
set_manifest_autogenerated_loc_conf_defaults(&ctx,
ctx.default_loc_config_container);
manifest_inherit_application_value_hierarchies(&ctx);
manifest_inherit_location_value_hierarchies(&ctx);
result = ctx.manifest;
deinit_config_manifest_generation(&ctx);
return result;
}
static void
init_config_manifest_generation(ngx_conf_t *cf, manifest_gen_ctx_t *ctx) {
ngx_memzero(ctx, sizeof(manifest_gen_ctx_t));
ctx->cf = cf;
ctx->manifest = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
ctx->empty_object = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
ctx->empty_array = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_ARRAY);
ctx->it = psg_json_value_iterator_new();
ctx->end = psg_json_value_iterator_new();
ctx->it2 = psg_json_value_iterator_new();
ctx->end2 = psg_json_value_iterator_new();
ctx->it3 = psg_json_value_iterator_new();
ctx->end3 = psg_json_value_iterator_new();
ctx->it4 = psg_json_value_iterator_new();
ctx->end4 = psg_json_value_iterator_new();
ctx->global_config_container = psg_json_value_set_value(ctx->manifest,
"global_configuration", -1, ctx->empty_object);
ctx->default_app_config_container = psg_json_value_set_value(ctx->manifest,
"default_application_configuration", -1, ctx->empty_object);
ctx->default_loc_config_container = psg_json_value_set_value(ctx->manifest,
"default_location_configuration", -1, ctx->empty_object);
ctx->app_configs_container = psg_json_value_set_value(ctx->manifest,
"application_configurations", -1, ctx->empty_object);
}
static void
deinit_config_manifest_generation(manifest_gen_ctx_t *ctx) {
psg_json_value_free(ctx->empty_object);
psg_json_value_free(ctx->empty_array);
psg_json_value_iterator_free(ctx->it);
psg_json_value_iterator_free(ctx->end);
psg_json_value_iterator_free(ctx->it2);
psg_json_value_iterator_free(ctx->end2);
psg_json_value_iterator_free(ctx->it3);
psg_json_value_iterator_free(ctx->end3);
psg_json_value_iterator_free(ctx->it4);
psg_json_value_iterator_free(ctx->end4);
}
static void
recursively_generate_config_manifest_for_loc_conf(manifest_gen_ctx_t *ctx,
passenger_loc_conf_t *plcf)
{
passenger_loc_conf_t **children;
ngx_http_core_srv_conf_t *cscf;
ngx_http_core_loc_conf_t *clcf;
ngx_uint_t i;
cscf = plcf->cscf;
clcf = plcf->clcf;
if (cscf != NULL && clcf != NULL && plcf->autogenerated.enabled) {
generate_config_manifest_for_loc_conf(ctx, plcf, cscf, clcf);
}
children = plcf->children.elts;
for (i = 0; i < plcf->children.nelts; i++) {
recursively_generate_config_manifest_for_loc_conf(ctx, children[i]);
}
}
static int
infer_loc_conf_app_group_name(manifest_gen_ctx_t *ctx, passenger_loc_conf_t *plcf,
ngx_http_core_loc_conf_t *clcf, ngx_str_t *result)
{
ngx_str_t app_root, app_env;
char *abs_path;
u_char *buf;
void *_unused;
size_t buf_size;
if (plcf->autogenerated.app_group_name.data == NULL) {
if (plcf->autogenerated.app_root.data == NULL) {
buf_size = clcf->root.len + sizeof("/..") - 1;
buf = (u_char *) ngx_pnalloc(ctx->cf->pool, buf_size);
if (buf == NULL) {
return 0;
}
app_root.data = buf;
app_root.len = ngx_snprintf(buf, buf_size, "%V/..", &clcf->root) - buf;
} else {
app_root = plcf->autogenerated.app_root;
}
abs_path = psg_absolutize_path(
(const char *) app_root.data, app_root.len,
(const char *) ctx->cf->cycle->prefix.data, ctx->cf->cycle->prefix.len,
&app_root.len);
app_root.data = (u_char *) ngx_pnalloc(ctx->cf->pool, app_root.len);
_unused = ngx_copy(app_root.data, abs_path, app_root.len);
(void) _unused; /* Shut up compiler warning */
free(abs_path);
if (plcf->autogenerated.environment.data == NULL) {
app_env.data = (u_char *) DEFAULT_APP_ENV;
app_env.len = sizeof(DEFAULT_APP_ENV) - 1;
} else {
app_env = plcf->autogenerated.environment;
}
buf_size = app_root.len + app_env.len + sizeof(" ()") - 1;
buf = (u_char *) ngx_pnalloc(ctx->cf->pool, buf_size);
result->data = buf;
result->len = ngx_snprintf(buf, buf_size, "%V (%V)", &app_root, &app_env) - buf;
} else {
*result = plcf->autogenerated.app_group_name;
}
return 1;
}
static u_char *
infer_default_app_root(manifest_gen_ctx_t *ctx, ngx_http_core_loc_conf_t *clcf,
size_t *len)
{
u_char *path, *end;
path = ngx_pnalloc(ctx->cf->temp_pool, clcf->root.len + 3);
end = ngx_snprintf(path, clcf->root.len + 3,
"%V/..", &clcf->root);
return (u_char *) psg_absolutize_path((const char *) path,
end - path, NULL, 0, len);
}
static PsgJsonValue *
find_or_create_manifest_app_config_container(manifest_gen_ctx_t *ctx,
ngx_str_t *app_group_name)
{
PsgJsonValue *result;
result = psg_json_value_get_or_create_null(ctx->app_configs_container,
(const char *) app_group_name->data, app_group_name->len);
if (psg_json_value_is_null(result)) {
psg_json_value_set_value(result, "options", -1, ctx->empty_object);
psg_json_value_set_value(result, "default_location_configuration", -1, ctx->empty_object);
psg_json_value_set_value(result, "location_configurations", -1, ctx->empty_array);
}
return result;
}
static PsgJsonValue *
find_or_create_manifest_loc_config_container(manifest_gen_ctx_t *ctx,
PsgJsonValue *app_config_container, ngx_http_core_srv_conf_t *cscf,
ngx_http_core_loc_conf_t *clcf)
{
PsgJsonValue *loc_configs_container;
PsgJsonValue *loc_config_container;
loc_configs_container = psg_json_value_get(app_config_container,
"location_configurations", -1);
loc_config_container = find_manifest_loc_config_container(ctx,
loc_configs_container, cscf, clcf);
if (loc_config_container == NULL) {
loc_config_container = create_manifest_loc_config_container(ctx,
loc_configs_container, cscf, clcf);
}
return loc_config_container;
}
static int
matches_any_server_names(manifest_gen_ctx_t *ctx, ngx_http_core_srv_conf_t *cscf,
PsgJsonValue *server_names_doc)
{
ngx_http_server_name_t *server_names = cscf->server_names.elts;
PsgJsonValue *server_name_doc;
ngx_str_t server_name;
ngx_uint_t i;
psg_json_value_begin(server_names_doc, ctx->it2);
psg_json_value_end(server_names_doc, ctx->end2);
while (!psg_json_value_iterator_eq(ctx->it2, ctx->end2)) {
server_name_doc = psg_json_value_iterator_get_value(ctx->it2);
server_name.data = (u_char *) psg_json_value_get_str(server_name_doc, &server_name.len);
for (i = 0; i < cscf->server_names.nelts; i++) {
if (server_names[i].name.len == server_name.len
&& ngx_strncasecmp(server_names[i].name.data, server_name.data,
server_name.len) == 0)
{
return 1;
}
}
psg_json_value_iterator_advance(ctx->it2);
}
return 0;
}
static PsgJsonValue *
find_manifest_loc_config_container(manifest_gen_ctx_t *ctx,
PsgJsonValue *loc_configs_container, ngx_http_core_srv_conf_t *cscf,
ngx_http_core_loc_conf_t *clcf)
{
PsgJsonValue *loc_config_container;
PsgJsonValue *vhost_doc;
PsgJsonValue *server_names_doc;
PsgJsonValue *location_matcher_doc;
ngx_str_t json_location_matcher_type, json_location_matcher_value;
psg_json_value_begin(loc_configs_container, ctx->it);
psg_json_value_end(loc_configs_container, ctx->end);
while (!psg_json_value_iterator_eq(ctx->it, ctx->end)) {
loc_config_container = psg_json_value_iterator_get_value(ctx->it);
vhost_doc = psg_json_value_get(loc_config_container, "web_server_virtual_host", -1);
location_matcher_doc = psg_json_value_get(loc_config_container, "location_matcher", -1);
json_location_matcher_type.data = (u_char *) psg_json_value_get_str(
psg_json_value_get(location_matcher_doc, "type", -1),
&json_location_matcher_type.len);
#if (NGX_PCRE)
if (clcf->regex != NULL) {
if (json_location_matcher_type.len != sizeof("regex") - 1
|| ngx_memcmp(json_location_matcher_type.data, "regex", sizeof("regex") - 1) != 0)
{
goto no_match;
}
} else
#endif
if (clcf->exact_match) {
if (json_location_matcher_type.len != sizeof("exact") - 1
|| ngx_memcmp(json_location_matcher_type.data, "exact", sizeof("exact") - 1) != 0)
{
goto no_match;
}
} else {
if (json_location_matcher_type.len != sizeof("prefix") - 1
|| ngx_memcmp(json_location_matcher_type.data, "prefix", sizeof("prefix") - 1) != 0)
{
goto no_match;
}
}
json_location_matcher_value.data = (u_char *) psg_json_value_get_str(
psg_json_value_get(location_matcher_doc, "value", -1),
&json_location_matcher_value.len);
if (ngx_memn2cmp(clcf->name.data, json_location_matcher_value.data,
clcf->name.len, json_location_matcher_value.len)
!= 0)
{
goto no_match;
}
server_names_doc = psg_json_value_get(vhost_doc, "server_names", -1);
if (!matches_any_server_names(ctx, cscf, server_names_doc)) {
goto no_match;
}
return loc_config_container;
no_match:
psg_json_value_iterator_advance(ctx->it);
}
return NULL;
}
static PsgJsonValue *
create_manifest_loc_config_container(manifest_gen_ctx_t *ctx,
PsgJsonValue *loc_configs_container, ngx_http_core_srv_conf_t *cscf,
ngx_http_core_loc_conf_t *clcf)
{
PsgJsonValue *loc_config_container = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
PsgJsonValue *vhost_doc = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
PsgJsonValue *server_names_doc = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_ARRAY);
PsgJsonValue *location_matcher_doc = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
PsgJsonValue *options_doc = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
PsgJsonValue *server_name_doc, *result;
ngx_http_server_name_t *server_names = cscf->server_names.elts;
ngx_uint_t i;
for (i = 0; i < cscf->server_names.nelts; i++) {
server_name_doc = psg_json_value_new_str((const char *) server_names[i].name.data,
server_names[i].name.len);
psg_json_value_append_val(server_names_doc, server_name_doc);
psg_json_value_free(server_name_doc);
}
psg_json_value_set_value(vhost_doc, "server_names", -1, server_names_doc);
psg_json_value_set_str(location_matcher_doc, "value",
(const char *) clcf->name.data, clcf->name.len);
#if (NGX_PCRE)
if (clcf->regex != NULL) {
psg_json_value_set_str(location_matcher_doc, "type",
"regex", -1);
} else
#endif
if (clcf->exact_match) {
psg_json_value_set_str(location_matcher_doc, "type",
"exact", -1);
} else {
psg_json_value_set_str(location_matcher_doc, "type",
"prefix", -1);
}
psg_json_value_set_value(loc_config_container, "web_server_virtual_host", -1, vhost_doc);
psg_json_value_set_value(loc_config_container, "location_matcher", -1, location_matcher_doc);
psg_json_value_set_value(loc_config_container, "options", -1, options_doc);
result = psg_json_value_append_val(loc_configs_container, loc_config_container);
psg_json_value_free(loc_config_container);
psg_json_value_free(vhost_doc);
psg_json_value_free(server_names_doc);
psg_json_value_free(location_matcher_doc);
psg_json_value_free(options_doc);
return result;
}
static PsgJsonValue *
find_or_create_manifest_option_container(manifest_gen_ctx_t *ctx,
PsgJsonValue *options_container, const char *option_name,
size_t option_name_len)
{
PsgJsonValue *result;
result = psg_json_value_get_or_create_null(options_container,
option_name, option_name_len);
if (psg_json_value_is_null(result)) {
init_manifest_option_container(ctx, result);
}
return result;
}
static void
init_manifest_option_container(manifest_gen_ctx_t *ctx, PsgJsonValue *doc) {
psg_json_value_set_value(doc, "value_hierarchy", -1, ctx->empty_array);
}
static PsgJsonValue *
add_manifest_option_container_hierarchy_member(PsgJsonValue *option_container,
ngx_str_t *source_file, ngx_uint_t source_line)
{
PsgJsonValue *value_hierarchy = psg_json_value_get(option_container, "value_hierarchy", -1);
PsgJsonValue *hierarchy_member = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
PsgJsonValue *source = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
PsgJsonValue *result;
psg_json_value_set_str(source, "type", "web-server-config", sizeof("web-server-config") - 1);
psg_json_value_set_str(source, "path", (const char *) source_file->data, source_file->len);
psg_json_value_set_uint(source, "line", source_line);
psg_json_value_set_value(hierarchy_member, "source", -1, source);
result = psg_json_value_append_val(value_hierarchy, hierarchy_member);
psg_json_value_free(hierarchy_member);
psg_json_value_free(source);
return result;
}
static void
generate_config_manifest_for_loc_conf(manifest_gen_ctx_t *ctx,
passenger_loc_conf_t *plcf, ngx_http_core_srv_conf_t *cscf,
ngx_http_core_loc_conf_t *clcf)
{
generate_config_manifest_for_autogenerated_loc_conf(ctx, plcf, cscf, clcf);
}
static void
find_or_create_manifest_app_and_loc_options_containers(manifest_gen_ctx_t *ctx,
passenger_loc_conf_t *plcf, ngx_http_core_srv_conf_t *cscf,
ngx_http_core_loc_conf_t *clcf, PsgJsonValue **app_options_container,
PsgJsonValue **loc_options_container)
{
ngx_str_t app_group_name;
PsgJsonValue *app_config_container, *loc_config_container;
ngx_str_t default_app_root;
if (*app_options_container != NULL && *loc_options_container != NULL) {
return;
}
if (cscf->server_name.len == 0) {
/* We are in the global context */
*app_options_container = ctx->default_app_config_container;
*loc_options_container = ctx->default_loc_config_container;
} else if (clcf->name.len == 0) {
/* We are in a server block */
infer_loc_conf_app_group_name(ctx, plcf, clcf, &app_group_name);
app_config_container = find_or_create_manifest_app_config_container(ctx, &app_group_name);
*app_options_container = psg_json_value_get(app_config_container, "options", -1);
*loc_options_container = psg_json_value_get(app_config_container, "default_location_configuration", -1);
/* Create a default value for passenger_app_group_name and
* passenger_app_root if we just created this config container
*/
if (psg_json_value_size(*app_options_container) == 0) {
add_manifest_options_container_inferred_default_str(ctx,
*app_options_container,
"passenger_app_group_name", -1,
(const char *) app_group_name.data, app_group_name.len);
default_app_root.data = infer_default_app_root(
ctx, clcf, &default_app_root.len);
add_manifest_options_container_inferred_default_str(ctx,
*app_options_container,
"passenger_app_root", -1,
(const char *) default_app_root.data, default_app_root.len);
free(default_app_root.data);
}
} else {
/* We are in a location/if block */
infer_loc_conf_app_group_name(ctx, plcf, clcf, &app_group_name);
app_config_container = find_or_create_manifest_app_config_container(ctx, &app_group_name);
loc_config_container = find_or_create_manifest_loc_config_container(ctx, app_config_container,
cscf, clcf);
*app_options_container = psg_json_value_get(app_config_container, "options", -1);
*loc_options_container = psg_json_value_get(loc_config_container, "options", -1);
}
}
static void
reverse_manifest_value_hierarchies(manifest_gen_ctx_t *ctx) {
PsgJsonValue *app_config_container;
PsgJsonValue *options_container;
PsgJsonValue *location_configs_container, *location_config_container;
reverse_value_hierarchies_in_options_container(ctx->global_config_container,
ctx->it, ctx->end);
reverse_value_hierarchies_in_options_container(ctx->default_app_config_container,
ctx->it, ctx->end);
reverse_value_hierarchies_in_options_container(ctx->default_loc_config_container,
ctx->it, ctx->end);
psg_json_value_begin(ctx->app_configs_container, ctx->it);
psg_json_value_end(ctx->app_configs_container, ctx->end);
while (!psg_json_value_iterator_eq(ctx->it, ctx->end)) {
app_config_container = psg_json_value_iterator_get_value(ctx->it);
options_container = psg_json_value_get(app_config_container,
"options", -1);
reverse_value_hierarchies_in_options_container(options_container,
ctx->it2, ctx->end2);
options_container = psg_json_value_get(app_config_container,
"default_location_configuration", -1);
reverse_value_hierarchies_in_options_container(options_container,
ctx->it2, ctx->end2);
location_configs_container = psg_json_value_get(app_config_container,
"location_configurations", -1);
if (location_configs_container != NULL) {
psg_json_value_begin(location_configs_container, ctx->it2);
psg_json_value_end(location_configs_container, ctx->end2);
while (!psg_json_value_iterator_eq(ctx->it2, ctx->end2)) {
location_config_container = psg_json_value_iterator_get_value(ctx->it2);
options_container = psg_json_value_get(location_config_container,
"options", -1);
reverse_value_hierarchies_in_options_container(options_container,
ctx->it3, ctx->end3);
psg_json_value_iterator_advance(ctx->it2);
}
}
psg_json_value_iterator_advance(ctx->it);
}
}
static void
reverse_value_hierarchies_in_options_container(PsgJsonValue *options_container,
PsgJsonValueIterator *it, PsgJsonValueIterator *end)
{
PsgJsonValue *option_container, *value_hierarchy_doc;
unsigned int i, len;
psg_json_value_begin(options_container, it);
psg_json_value_end(options_container, end);
while (!psg_json_value_iterator_eq(it, end)) {
option_container = psg_json_value_iterator_get_value(it);
value_hierarchy_doc = psg_json_value_get(option_container,
"value_hierarchy", -1);
len = psg_json_value_size(value_hierarchy_doc);
for (i = 0; i < len / 2; i++) {
psg_json_value_swap(
psg_json_value_get_at_index(value_hierarchy_doc, i),
psg_json_value_get_at_index(value_hierarchy_doc, len - i - 1));
}
psg_json_value_iterator_advance(it);
}
}
static void
add_manifest_options_container_dynamic_default(manifest_gen_ctx_t *ctx,
PsgJsonValue *options_container,
const char *option_name, size_t option_name_len,
const char *desc, size_t desc_len)
{
PsgJsonValue *option_container, *hierarchy, *hierarchy_member, *source;
option_container = psg_json_value_get_or_create_null(options_container, option_name, option_name_len);
if (psg_json_value_is_null(option_container)) {
init_manifest_option_container(ctx, option_container);
}
hierarchy = psg_json_value_get(option_container, "value_hierarchy", -1);
source = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
psg_json_value_set_str(source, "type", "dynamic-default-description", -1);
hierarchy_member = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
psg_json_value_set_value(hierarchy_member, "source", -1, source);
psg_json_value_set_str(hierarchy_member, "value", desc, desc_len);
psg_json_value_append_val(hierarchy, hierarchy_member);
psg_json_value_free(hierarchy_member);
psg_json_value_free(source);
}
static PsgJsonValue *
add_manifest_options_container_default(manifest_gen_ctx_t *ctx,
PsgJsonValue *options_container, const char *default_type,
const char *option_name, size_t option_name_len)
{
PsgJsonValue *option_container, *hierarchy, *hierarchy_member, *source, *result;
option_container = psg_json_value_get_or_create_null(options_container, option_name, option_name_len);
if (psg_json_value_is_null(option_container)) {
init_manifest_option_container(ctx, option_container);
}
hierarchy = psg_json_value_get(option_container, "value_hierarchy", -1);
source = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
psg_json_value_set_str(source, "type", default_type, -1);
hierarchy_member = psg_json_value_new_with_type(PSG_JSON_VALUE_TYPE_OBJECT);
psg_json_value_set_value(hierarchy_member, "source", -1, source);
result = psg_json_value_append_val(hierarchy, hierarchy_member);
psg_json_value_free(hierarchy_member);
psg_json_value_free(source);
return result;
}
static void
add_manifest_options_container_static_default_str(manifest_gen_ctx_t *ctx,
PsgJsonValue *options_container, const char *option_name, size_t option_name_len,
const char *value, size_t value_len)
{
PsgJsonValue *hierarchy_member = add_manifest_options_container_default(
ctx, options_container, "default", option_name, option_name_len);
psg_json_value_set_str(hierarchy_member, "value", value, value_len);
}
static void
add_manifest_options_container_static_default_int(manifest_gen_ctx_t *ctx,
PsgJsonValue *options_container, const char *option_name, size_t option_name_len,
int value)
{
PsgJsonValue *hierarchy_member = add_manifest_options_container_default(
ctx, options_container, "default", option_name, option_name_len);
psg_json_value_set_int(hierarchy_member, "value", value);
}
static void
add_manifest_options_container_static_default_uint(manifest_gen_ctx_t *ctx,
PsgJsonValue *options_container, const char *option_name, size_t option_name_len,
unsigned int value)
{
PsgJsonValue *hierarchy_member = add_manifest_options_container_default(
ctx, options_container, "default", option_name, option_name_len);
psg_json_value_set_uint(hierarchy_member, "value", value);
}
static void
add_manifest_options_container_static_default_bool(manifest_gen_ctx_t *ctx,
PsgJsonValue *options_container, const char *option_name, size_t option_name_len,
int value)
{
PsgJsonValue *hierarchy_member = add_manifest_options_container_default(
ctx, options_container, "default", option_name, option_name_len);
psg_json_value_set_bool(hierarchy_member, "value", value);
}
static void
add_manifest_options_container_inferred_default_str(manifest_gen_ctx_t *ctx,
PsgJsonValue *options_container, const char *option_name, size_t option_name_len,
const char *value, size_t value_len)
{
PsgJsonValue *hierarchy_member = add_manifest_options_container_default(
ctx, options_container, "inferred-default", option_name, option_name_len);
psg_json_value_set_str(hierarchy_member, "value", value, value_len);
}
static void
manifest_inherit_application_value_hierarchies(manifest_gen_ctx_t *ctx) {
PsgJsonValue *app_config_container, *options_container, *option_container, *default_app_config;
PsgJsonValue *value_hierarchy_doc, *value_hierarchy_from_default;
const char *option_name;
size_t option_name_len;
/* Iterate through all 'application_configurations' objects */
psg_json_value_begin(ctx->app_configs_container, ctx->it);
psg_json_value_end(ctx->app_configs_container, ctx->end);
while (!psg_json_value_iterator_eq(ctx->it, ctx->end)) {
app_config_container = psg_json_value_iterator_get_value(ctx->it);
/* Iterate through all its 'options' objects */
options_container = psg_json_value_get(app_config_container, "options", -1);
psg_json_value_begin(options_container, ctx->it2);
psg_json_value_end(options_container, ctx->end2);
while (!psg_json_value_iterator_eq(ctx->it2, ctx->end2)) {
/* For each option, inherit the value hierarchies
* from the 'default_application_configuration' object.
*
* Since the value hierarchy array is already in
* most-to-least-specific order, simply appending
* the 'default_application_configuration' hierarchy is
* enough.
*/
option_name = psg_json_value_iterator_get_name(ctx->it2,
&option_name_len);
option_container = psg_json_value_iterator_get_value(ctx->it2);
default_app_config = psg_json_value_get(ctx->default_app_config_container,
option_name, option_name_len);
if (default_app_config != NULL) {
value_hierarchy_doc = psg_json_value_get(option_container,
"value_hierarchy", -1);
value_hierarchy_from_default = psg_json_value_get(default_app_config,
"value_hierarchy", -1);
psg_json_value_append_vals(value_hierarchy_doc,
value_hierarchy_from_default,
ctx->it3, ctx->end3);
maybe_inherit_string_array_hierarchy_values(value_hierarchy_doc,
ctx->it3, ctx->end3);
maybe_inherit_string_keyval_hierarchy_values(value_hierarchy_doc,
ctx->it3, ctx->end3);
}
psg_json_value_iterator_advance(ctx->it2);
}
/* Iterate through all 'default_application_configuration' options */
psg_json_value_begin(ctx->default_app_config_container, ctx->it2);
psg_json_value_end(ctx->default_app_config_container, ctx->end2);
while (!psg_json_value_iterator_eq(ctx->it2, ctx->end2)) {
/* For each default app config object, if there is no object in
* the current context's 'options' with the same name, then add
* it there.
*/
option_name = psg_json_value_iterator_get_name(ctx->it2,
&option_name_len);
if (!psg_json_value_is_member(options_container, option_name, option_name_len)) {
option_container = psg_json_value_iterator_get_value(ctx->it2);
psg_json_value_set_value(options_container, option_name, option_name_len,
option_container);
}
psg_json_value_iterator_advance(ctx->it2);
}
psg_json_value_iterator_advance(ctx->it);
}
}
static void
manifest_inherit_location_value_hierarchies(manifest_gen_ctx_t *ctx) {
PsgJsonValue *app_config_container, *location_configs_container, *location_config_container;
PsgJsonValue *app_default_location_configs;
PsgJsonValue *options_container, *option_container, *default_location_config;
PsgJsonValue *value_hierarchy_doc, *value_hierarchy_from_default;
const char *option_name;
size_t option_name_len;
/* Iterate through all 'application_configurations' objects */
psg_json_value_begin(ctx->app_configs_container, ctx->it);
psg_json_value_end(ctx->app_configs_container, ctx->end);
while (!psg_json_value_iterator_eq(ctx->it, ctx->end)) {
app_config_container = psg_json_value_iterator_get_value(ctx->it);
/* Iterate through all its 'default_location_configuration' options */
app_default_location_configs = psg_json_value_get(app_config_container,
"default_location_configuration", -1);
options_container = app_default_location_configs;
psg_json_value_begin(options_container, ctx->it2);
psg_json_value_end(options_container, ctx->end2);
while (!psg_json_value_iterator_eq(ctx->it2, ctx->end2)) {
/* For each option, inherit the value hierarchies
* from the top-level 'default_application_configuration' object.
*
* Since the value hierarchy array is already in
* most-to-least-specific order, simply appending
* the 'default_application_configuration' hierarchy is
* enough.
*/
option_name = psg_json_value_iterator_get_name(ctx->it2, &option_name_len);
option_container = psg_json_value_iterator_get_value(ctx->it2);
default_location_config = psg_json_value_get(ctx->default_loc_config_container,
option_name, option_name_len);
if (default_location_config != NULL) {
value_hierarchy_doc = psg_json_value_get(option_container,
"value_hierarchy", -1);
value_hierarchy_from_default = psg_json_value_get(default_location_config,
"value_hierarchy", -1);
psg_json_value_append_vals(value_hierarchy_doc,
value_hierarchy_from_default,
ctx->it3, ctx->end3);
maybe_inherit_string_array_hierarchy_values(value_hierarchy_doc,
ctx->it3, ctx->end3);
maybe_inherit_string_keyval_hierarchy_values(value_hierarchy_doc,
ctx->it3, ctx->end3);
}
psg_json_value_iterator_advance(ctx->it2);
}
/* Iterate through all top-level 'default_location_configuration' options */
psg_json_value_begin(ctx->default_loc_config_container, ctx->it2);
psg_json_value_end(ctx->default_loc_config_container, ctx->end2);
while (!psg_json_value_iterator_eq(ctx->it2, ctx->end2)) {
/* For each default top-level 'default_location_configuration' option,
* if there is no object in the current context's 'default_application_configuration'
* with the same name, then add it there.
*/
option_name = psg_json_value_iterator_get_name(ctx->it2,
&option_name_len);
if (!psg_json_value_is_member(options_container, option_name, option_name_len)) {
option_container = psg_json_value_iterator_get_value(ctx->it2);
psg_json_value_set_value(options_container, option_name, option_name_len,
option_container);
}
psg_json_value_iterator_advance(ctx->it2);
}
/* Iterate through all its 'locations_configurations' options */
location_configs_container = psg_json_value_get(app_config_container,
"location_configurations", -1);
if (location_configs_container != NULL) {
psg_json_value_begin(location_configs_container, ctx->it2);
psg_json_value_end(location_configs_container, ctx->end2);
while (!psg_json_value_iterator_eq(ctx->it2, ctx->end2)) {
location_config_container = psg_json_value_iterator_get_value(ctx->it2);
options_container = psg_json_value_get(location_config_container,
"options", -1);
psg_json_value_begin(options_container, ctx->it3);
psg_json_value_end(options_container, ctx->end3);
while (!psg_json_value_iterator_eq(ctx->it3, ctx->end3)) {
/* For each option, inherit the value hierarchies
* from the 'default_location_configuration' belonging
* to the current app (which also contains the global
* location config defaults).
*
* Since the value hierarchy array is already in
* most-to-least-specific order, simply appending
* the 'default_location_configuration' hierarchy is
* enough.
*/
option_name = psg_json_value_iterator_get_name(ctx->it3, &option_name_len);
option_container = psg_json_value_iterator_get_value(ctx->it3);
default_location_config = psg_json_value_get(app_default_location_configs,
option_name, option_name_len);
if (default_location_config != NULL) {
value_hierarchy_doc = psg_json_value_get(option_container,
"value_hierarchy", -1);
value_hierarchy_from_default = psg_json_value_get(default_location_config,
"value_hierarchy", -1);
psg_json_value_append_vals(value_hierarchy_doc,
value_hierarchy_from_default,
ctx->it4, ctx->end4);
maybe_inherit_string_array_hierarchy_values(value_hierarchy_doc,
ctx->it4, ctx->end4);
maybe_inherit_string_keyval_hierarchy_values(value_hierarchy_doc,
ctx->it4, ctx->end4);
}
psg_json_value_iterator_advance(ctx->it3);
}
psg_json_value_iterator_advance(ctx->it2);
}
}
psg_json_value_iterator_advance(ctx->it);
}
}
static void
maybe_inherit_string_array_hierarchy_values(PsgJsonValue *value_hierarchy_doc,
PsgJsonValueIterator *it, PsgJsonValueIterator *end)
{
PsgJsonValue *value, *current, *next, *current_value, *next_value;
unsigned int len;
int i;
if (psg_json_value_size(value_hierarchy_doc) == 0) {
return;
}
value = psg_json_value_get(psg_json_value_get_at_index(value_hierarchy_doc, 0),
"value", -1);
if (psg_json_value_type(value) != PSG_JSON_VALUE_TYPE_ARRAY) {
return;
}
len = psg_json_value_size(value_hierarchy_doc);
for (i = len - 1; i >= 1; i--) {
current = psg_json_value_get_at_index(value_hierarchy_doc, i);
next = psg_json_value_get_at_index(value_hierarchy_doc, i - 1);
current_value = psg_json_value_get(current, "value", -1);
next_value = psg_json_value_get(next, "value", -1);
psg_json_value_begin(current_value, it);
psg_json_value_end(current_value, end);
while (!psg_json_value_iterator_eq(it, end)) {
if (!json_array_contains(next_value, psg_json_value_iterator_get_value(it))) {
psg_json_value_append_val(next_value, psg_json_value_iterator_get_value(it));
}
psg_json_value_iterator_advance(it);
}
}
}
static void
maybe_inherit_string_keyval_hierarchy_values(PsgJsonValue *value_hierarchy_doc,
PsgJsonValueIterator *it, PsgJsonValueIterator *end)
{
PsgJsonValue *value, *current, *next, *current_value, *next_value;
const char *name;
size_t name_len;
unsigned int len;
int i;
if (psg_json_value_size(value_hierarchy_doc) == 0) {
return;
}
value = psg_json_value_get(psg_json_value_get_at_index(value_hierarchy_doc, 0),
"value", -1);
if (psg_json_value_type(value) != PSG_JSON_VALUE_TYPE_OBJECT) {
return;
}
len = psg_json_value_size(value_hierarchy_doc);
for (i = len - 1; i >= 1; i--) {
current = psg_json_value_get_at_index(value_hierarchy_doc, i);
next = psg_json_value_get_at_index(value_hierarchy_doc, i - 1);
current_value = psg_json_value_get(current, "value", -1);
next_value = psg_json_value_get(next, "value", -1);
psg_json_value_begin(current_value, it);
psg_json_value_end(current_value, end);
while (!psg_json_value_iterator_eq(it, end)) {
name = psg_json_value_iterator_get_name(it, &name_len);
if (!psg_json_value_is_member(next_value, name, name_len)) {
psg_json_value_set_value(next_value, name, name_len,
psg_json_value_iterator_get_value(it));
}
psg_json_value_iterator_advance(it);
}
}
}
static void
psg_json_value_append_vals(PsgJsonValue *doc, PsgJsonValue *doc2,
PsgJsonValueIterator *it, PsgJsonValueIterator *end)
{
PsgJsonValue *elem;
psg_json_value_begin(doc2, it);
psg_json_value_end(doc2, end);
while (!psg_json_value_iterator_eq(it, end)) {
elem = psg_json_value_iterator_get_value(it);
psg_json_value_append_val(doc, elem);
psg_json_value_iterator_advance(it);
}
}
static int
json_array_contains(PsgJsonValue *doc, PsgJsonValue *elem) {
unsigned int i, len;
PsgJsonValue *current;
len = psg_json_value_size(doc);
for (i = 0; i < len; i++) {
current = psg_json_value_get_at_index(doc, i);
if (psg_json_value_eq(current, elem)) {
return 1;
}
}
return 0;
}