diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/abi_compat.c | 123 | ||||
-rw-r--r-- | src/error.c | 13 | ||||
-rw-r--r-- | src/error.h | 3 | ||||
-rw-r--r-- | src/generate.c | 93 | ||||
-rw-r--r-- | src/netplan.c | 226 | ||||
-rw-r--r-- | src/networkd.c | 3 | ||||
-rw-r--r-- | src/parse-globals.h | 18 | ||||
-rw-r--r-- | src/parse-nm.c | 5 | ||||
-rw-r--r-- | src/parse.c | 1419 | ||||
-rw-r--r-- | src/types.c | 53 | ||||
-rw-r--r-- | src/types.h | 71 | ||||
-rw-r--r-- | src/util-internal.h | 8 | ||||
-rw-r--r-- | src/util.c | 77 | ||||
-rw-r--r-- | src/validation.c | 84 | ||||
-rw-r--r-- | src/validation.h | 6 | ||||
-rw-r--r-- | src/yaml-helpers.h | 20 |
16 files changed, 1259 insertions, 963 deletions
diff --git a/src/abi_compat.c b/src/abi_compat.c index dea044a..988fe9c 100644 --- a/src/abi_compat.c +++ b/src/abi_compat.c @@ -25,10 +25,12 @@ #include "types.h" #include "util-internal.h" #include "parse-nm.h" +#include "parse-globals.h" #include "names.h" #include "networkd.h" #include "nm.h" #include "openvswitch.h" +#include "util.h" #include <unistd.h> #include <glib.h> @@ -59,11 +61,13 @@ char _global_backend_off[8+offsetof(struct netplan_state, backend)] = {}; NETPLAN_ABI NetplanState global_state = {}; +// LCOV_EXCL_START NetplanBackend netplan_get_global_backend() { return netplan_state_get_backend(&global_state); } +// LCOV_EXCL_STOP /** * Clear NetplanNetDefinition hashtable @@ -73,9 +77,11 @@ netplan_clear_netdefs() { guint n = netplan_state_get_netdefs_size(&global_state); netplan_state_reset(&global_state); + netplan_parser_reset(&global_parser); return n; } +// LCOV_EXCL_START NETPLAN_INTERNAL void write_network_file(const NetplanNetDefinition* def, const char* rootdir, const char* path) { @@ -115,7 +121,6 @@ cleanup_networkd_conf(const char* rootdir) // There only for compatibility purposes, the proper implementation is now directly // in the `generate` binary. -// LCOV_EXCL_START NETPLAN_ABI void enable_networkd(const char* generator_dir) { @@ -134,7 +139,6 @@ enable_networkd(const char* generator_dir) exit(1); } } -// LCOV_EXCL_STOP NETPLAN_INTERNAL void write_nm_conf(NetplanNetDefinition* def, const char* rootdir) @@ -181,3 +185,118 @@ cleanup_ovs_conf(const char* rootdir) { netplan_ovs_cleanup(rootdir); } +// LCOV_EXCL_STOP + +gboolean +netplan_parse_yaml(const char* filename, GError** error) +{ + return netplan_parser_load_yaml(&global_parser, filename, error); +} + +/** + * Post-processing after parsing all config files + */ +GHashTable * +netplan_finish_parse(GError** error) +{ + if (netplan_state_import_parser_results(&global_state, &global_parser, error)) + return global_state.netdefs; + return NULL; +} + +/** + * Generate the Netplan YAML configuration for the selected netdef + * @def: NetplanNetDefinition (as pointer), the data to be serialized + * @rootdir: If not %NULL, generate configuration in this root directory + * (useful for testing). + */ +void +write_netplan_conf(const NetplanNetDefinition* def, const char* rootdir) +{ + netplan_netdef_write_yaml(&global_state, def, rootdir, NULL); +} + +gboolean +netplan_state_write_yaml(const NetplanState* np_state, const char* file_hint, const char* rootdir, GError** error); + +/** + * Generate the Netplan YAML configuration for all currently parsed netdefs + * @file_hint: Name hint for the generated output YAML file + * @rootdir: If not %NULL, generate configuration in this root directory + * (useful for testing). + */ +NETPLAN_ABI void +write_netplan_conf_full(const char* file_hint, const char* rootdir) +{ + netplan_finish_parse(NULL); + netplan_state_write_yaml(&global_state, file_hint, rootdir, NULL); +} + +NETPLAN_PUBLIC gboolean +netplan_parse_keyfile(const char* filename, GError** error) +{ + return netplan_parser_load_keyfile(&global_parser, filename, error); +} + +// LCOV_EXCL_START +void process_input_file(const char *f) +{ + GError* error = NULL; + + g_debug("Processing input file %s..", f); + if (!netplan_parser_load_yaml(&global_parser, f, &error)) { + g_fprintf(stderr, "%s\n", error->message); + exit(1); + } +} + +gboolean +process_yaml_hierarchy(const char* rootdir) +{ + GError* error = NULL; + if (!netplan_parser_load_yaml_hierarchy(&global_parser, rootdir, &error)) { + g_fprintf(stderr, "%s\n", error->message); + exit(1); + } + return TRUE; +} +// LCOV_EXCL_STOP + +/** + * Helper function for testing only + */ +NETPLAN_INTERNAL void +_write_netplan_conf(const char* netdef_id, const char* rootdir) +{ + GHashTable* ht = NULL; + const NetplanNetDefinition* def = NULL; + ht = netplan_finish_parse(NULL); + def = g_hash_table_lookup(ht, netdef_id); + write_netplan_conf(def, rootdir); +} + +/** + * Get the filename from which the given netdef has been parsed. + * @rootdir: ID of the netdef to be looked up + * @rootdir: parse files from this root directory + */ +gchar* +netplan_get_filename_by_id(const char* netdef_id, const char* rootdir) +{ + NetplanParser* npp = netplan_parser_new(); + NetplanState* np_state = netplan_state_new(); + char *filename = NULL; + GError* error = NULL; + + if (!netplan_parser_load_yaml_hierarchy(npp, rootdir, &error) || + !netplan_state_import_parser_results(np_state, npp, &error)) { + g_fprintf(stderr, "%s\n", error->message); + return NULL; + } + netplan_parser_clear(&npp); + + netplan_state_get_netdef(np_state, netdef_id); + filename = g_strdup(netplan_netdef_get_filename(netplan_state_get_netdef(np_state, netdef_id))); + netplan_state_clear(&np_state); + return filename; +} diff --git a/src/error.c b/src/error.c index ff28be9..85f29ba 100644 --- a/src/error.c +++ b/src/error.c @@ -22,8 +22,7 @@ #include <yaml.h> #include "parse.h" -#include "parse-globals.h" - +#include "types.h" /**************************************************** * Loading and error handling @@ -41,10 +40,10 @@ write_error_marker(GString *message, int column) } static char * -get_syntax_error_context(const int line_num, const int column, GError **error) +get_syntax_error_context(const NetplanParser* npp, const int line_num, const int column, GError **error) { GString *message = NULL; - GFile *cur_file = g_file_new_for_path(current_file); + GFile *cur_file = g_file_new_for_path(npp->current.filename); GFileInputStream *file_stream; GDataInputStream *stream; gsize len; @@ -147,7 +146,7 @@ parser_error(const yaml_parser_t* parser, const char* yaml, GError** error) * Put a YAML specific error message for @node into @error. */ gboolean -yaml_error(const yaml_node_t* node, GError** error, const char* msg, ...) +yaml_error(const NetplanParser *npp, const yaml_node_t* node, GError** error, const char* msg, ...) { va_list argp; char* s; @@ -156,10 +155,10 @@ yaml_error(const yaml_node_t* node, GError** error, const char* msg, ...) va_start(argp, msg); g_vasprintf(&s, msg, argp); if (node != NULL) { - error_context = get_syntax_error_context(node->start_mark.line, node->start_mark.column, error); + error_context = get_syntax_error_context(npp, node->start_mark.line, node->start_mark.column, error); g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "%s:%zu:%zu: Error in network definition: %s\n%s", - current_file, + npp->current.filename, node->start_mark.line + 1, node->start_mark.column + 1, s, diff --git a/src/error.h b/src/error.h index 68061d8..7f1d9c2 100644 --- a/src/error.h +++ b/src/error.h @@ -22,10 +22,11 @@ #include <gio/gio.h> #include <yaml.h> +#include "parse.h" gboolean parser_error(const yaml_parser_t* parser, const char* yaml, GError** error); gboolean -yaml_error(const yaml_node_t* node, GError** error, const char* msg, ...); +yaml_error(const NetplanParser *npp, const yaml_node_t* node, GError** error, const char* msg, ...); diff --git a/src/generate.c b/src/generate.c index bc024db..c15a380 100644 --- a/src/generate.c +++ b/src/generate.c @@ -29,7 +29,6 @@ #include "util.h" #include "util-internal.h" #include "parse.h" -#include "parse-globals.h" #include "names.h" #include "networkd.h" #include "nm.h" @@ -38,7 +37,7 @@ static gchar* rootdir; static gchar** files; -static gboolean any_networkd; +static gboolean any_networkd = FALSE; static gboolean any_sriov; static gchar* mapping_iface; @@ -114,22 +113,8 @@ start_unit_jit(gchar *unit) }; // LCOV_EXCL_STOP -static void -nd_iterator_list(gpointer value, gpointer user_data) -{ - NetplanNetDefinition* def = (NetplanNetDefinition*) value; - if (write_networkd_conf(def, (const char*) user_data)) - any_networkd = TRUE; - - write_ovs_conf(def, (const char*) user_data); - write_nm_conf(def, (const char*) user_data); - if (def->sriov_explicit_vf_count < G_MAXUINT || def->sriov_link) - any_sriov = TRUE; -} - - static int -find_interface(gchar* interface) +find_interface(gchar* interface, GHashTable* netdefs) { GPtrArray *found; GFileInfo *info; @@ -204,6 +189,14 @@ exit_find: return ret; } +#define CHECK_CALL(call) {\ + if (!call) {\ + error_code = 1; \ + fprintf(stderr, "%s\n", error->message); \ + goto cleanup;\ + }\ +} + int main(int argc, char** argv) { GError* error = NULL; @@ -212,6 +205,9 @@ int main(int argc, char** argv) gboolean called_as_generator = (strstr(argv[0], "systemd/system-generators/") != NULL); g_autofree char* generator_run_stamp = NULL; glob_t gl; + int error_code = 0; + NetplanParser* npp = NULL; + NetplanState* np_state = NULL; /* Parse CLI options */ opt_context = g_option_context_new(NULL); @@ -226,7 +222,7 @@ int main(int argc, char** argv) g_option_context_add_main_entries(opt_context, options, NULL); if (!g_option_context_parse(opt_context, &argc, &argv, &error)) { - g_fprintf(stderr, "failed to parse options: %s\n", error->message); + fprintf(stderr, "failed to parse options: %s\n", error->message); return 1; } @@ -242,34 +238,48 @@ int main(int argc, char** argv) } } + npp = netplan_parser_new(); /* Read all input files */ if (files && !called_as_generator) { - for (gchar** f = files; f && *f; ++f) - process_input_file(*f); - } else if (!process_yaml_hierarchy(rootdir)) - return 1; // LCOV_EXCL_LINE - - netdefs = netplan_finish_parse(&error); - if (error) { - g_fprintf(stderr, "%s\n", error->message); - exit(1); - } + for (gchar** f = files; f && *f; ++f) { + CHECK_CALL(netplan_parser_load_yaml(npp, *f, &error)); + } + } else + CHECK_CALL(netplan_parser_load_yaml_hierarchy(npp, rootdir, &error)); + + np_state = netplan_state_new(); + CHECK_CALL(netplan_state_import_parser_results(np_state, npp, &error)); /* Clean up generated config from previous runs */ - cleanup_networkd_conf(rootdir); - cleanup_nm_conf(rootdir); - cleanup_ovs_conf(rootdir); + netplan_networkd_cleanup(rootdir); + netplan_nm_cleanup(rootdir); + netplan_ovs_cleanup(rootdir); + cleanup_sriov_conf(rootdir); - if (mapping_iface && netdefs) - return find_interface(mapping_iface); + if (mapping_iface && np_state->netdefs) { + error_code = find_interface(mapping_iface, np_state->netdefs); + goto cleanup; + } /* Generate backend specific configuration files from merged data. */ - write_ovs_conf_finish(rootdir); // OVS cleanup unit is always written - if (netdefs) { + CHECK_CALL(netplan_state_finish_ovs_write(np_state, rootdir, &error)); // OVS cleanup unit is always written + if (np_state->netdefs) { g_debug("Generating output files.."); - g_list_foreach (netdefs_ordered, nd_iterator_list, rootdir); - write_nm_conf_finish(rootdir); + for (GList* iterator = np_state->netdefs_ordered; iterator; iterator = iterator->next) { + NetplanNetDefinition* def = (NetplanNetDefinition*) iterator->data; + gboolean has_been_written = FALSE; + CHECK_CALL(netplan_netdef_write_networkd(np_state, def, rootdir, &has_been_written, &error)); + any_networkd = any_networkd || has_been_written; + + CHECK_CALL(netplan_netdef_write_ovs(np_state, def, rootdir, &has_been_written, &error)); + CHECK_CALL(netplan_netdef_write_nm(np_state, def, rootdir, &has_been_written, &error)); + + if (def->sriov_explicit_vf_count < G_MAXUINT || def->sriov_link) + any_sriov = TRUE; + } + + CHECK_CALL(netplan_state_finish_nm_write(np_state, rootdir, &error)); if (any_sriov) write_sriov_conf_finish(rootdir); /* We may have written .rules & .link files, thus we must * invalidate udevd cache of its config as by default it only @@ -282,7 +292,7 @@ int main(int argc, char** argv) /* Disable /usr/lib/NetworkManager/conf.d/10-globally-managed-devices.conf * (which restricts NM to wifi and wwan) if global renderer is NM */ - if (netplan_get_global_backend() == NETPLAN_BACKEND_NM) + if (netplan_state_get_backend(np_state) == NETPLAN_BACKEND_NM) g_string_free_to_file(g_string_new(NULL), rootdir, "/run/NetworkManager/conf.d/10-globally-managed-devices.conf", NULL); if (called_as_generator) { @@ -324,5 +334,10 @@ int main(int argc, char** argv) // LCOV_EXCL_STOP } - return 0; +cleanup: + if (npp) + netplan_parser_clear(&npp); + if (np_state) + netplan_state_clear(&np_state); + return error_code; } diff --git a/src/netplan.c b/src/netplan.c index 6285aa2..6dbf1d8 100644 --- a/src/netplan.c +++ b/src/netplan.c @@ -20,7 +20,6 @@ #include "netplan.h" #include "parse.h" -#include "parse-globals.h" #include "yaml-helpers.h" #include "names.h" @@ -36,7 +35,7 @@ write_match(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDefini YAML_STRING(event, emitter, "driver", def->match.driver) YAML_MAPPING_CLOSE(event, emitter); return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } static gboolean @@ -56,7 +55,7 @@ write_auth(yaml_event_t* event, yaml_emitter_t* emitter, NetplanAuthenticationSe YAML_STRING(event, emitter, "password", auth.password); YAML_MAPPING_CLOSE(event, emitter); return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } static gboolean @@ -118,7 +117,7 @@ write_bond_params(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNet YAML_MAPPING_CLOSE(event, emitter); } return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } static gboolean @@ -173,7 +172,7 @@ write_bridge_params(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanN YAML_MAPPING_CLOSE(event, emitter); } return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } static gboolean @@ -192,7 +191,7 @@ write_modem_params(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNe YAML_STRING(event, emitter, "password", def->modem_params.password); YAML_STRING(event, emitter, "number", def->modem_params.number); return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } typedef struct { @@ -206,7 +205,7 @@ _passthrough_handler(GQuark key_id, gpointer value, gpointer user_data) _passthrough_handler_data *d = user_data; const gchar* key = g_quark_to_string(key_id); YAML_STRING(d->event, d->emitter, key, value); -error: return; // LCOV_EXCL_LINE +err_path: return; // LCOV_EXCL_LINE } static gboolean @@ -228,7 +227,7 @@ write_backend_settings(yaml_event_t* event, yaml_emitter_t* emitter, NetplanBack YAML_MAPPING_CLOSE(event, emitter); } return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } static gboolean @@ -258,12 +257,12 @@ write_access_points(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanN write_auth(event, emitter, ap->auth); if (ap->mode != NETPLAN_WIFI_MODE_INFRASTRUCTURE) YAML_STRING(event, emitter, "mode", netplan_wifi_mode_name(ap->mode)); - if (!write_backend_settings(event, emitter, ap->backend_settings)) goto error; + if (!write_backend_settings(event, emitter, ap->backend_settings)) goto err_path; YAML_MAPPING_CLOSE(event, emitter); } YAML_MAPPING_CLOSE(event, emitter); return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } static gboolean @@ -294,7 +293,7 @@ write_addresses(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDe YAML_SEQUENCE_CLOSE(event, emitter); return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } static gboolean @@ -326,7 +325,7 @@ write_nameservers(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNet } YAML_MAPPING_CLOSE(event, emitter); return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } static gboolean @@ -364,7 +363,7 @@ write_dhcp_overrides(yaml_event_t* event, yaml_emitter_t* emitter, const char* k YAML_MAPPING_CLOSE(event, emitter); } return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } static gboolean @@ -426,7 +425,7 @@ write_tunnel_settings(yaml_event_t* event, yaml_emitter_t* emitter, const Netpla YAML_SEQUENCE_CLOSE(event, emitter); } return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } static gboolean @@ -489,7 +488,7 @@ write_routes(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDefin } return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } static gboolean @@ -593,11 +592,15 @@ write_openvswitch(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanOVS } return TRUE; -error: return FALSE; // LCOV_EXCL_LINE +err_path: return FALSE; // LCOV_EXCL_LINE } -void -_serialize_yaml(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDefinition* def) +static void +_serialize_yaml( + const NetplanState* np_state, + yaml_event_t* event, + yaml_emitter_t* emitter, + const NetplanNetDefinition* def) { GArray* tmp_arr = NULL; GHashTableIter iter; @@ -680,7 +683,7 @@ _serialize_yaml(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDe /* Search interfaces */ if (def->type == NETPLAN_DEF_TYPE_BRIDGE || def->type == NETPLAN_DEF_TYPE_BOND) { tmp_arr = g_array_new(FALSE, FALSE, sizeof(NetplanNetDefinition*)); - g_hash_table_iter_init(&iter, netdefs); + g_hash_table_iter_init(&iter, np_state->netdefs); while (g_hash_table_iter_next (&iter, &key, &value)) { NetplanNetDefinition *nd = (NetplanNetDefinition *) value; if (g_strcmp0(nd->bond, def->id) == 0 || g_strcmp0(nd->bridge, def->id) == 0) @@ -800,18 +803,18 @@ _serialize_yaml(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDe write_modem_params(event, emitter, def); if (def->type == NETPLAN_DEF_TYPE_WIFI) - if (!write_access_points(event, emitter, def)) goto error; + if (!write_access_points(event, emitter, def)) goto err_path; /* Handle devices in full fallback/passthrough mode (i.e. 'nm-devices') */ only_passthrough: - if (!write_backend_settings(event, emitter, def->backend_settings)) goto error; + if (!write_backend_settings(event, emitter, def->backend_settings)) goto err_path; /* Close remaining mappings */ YAML_MAPPING_CLOSE(event, emitter); return; // LCOV_EXCL_START -error: +err_path: g_warning("Error generating YAML: %s", emitter->problem); return; // LCOV_EXCL_STOP @@ -819,22 +822,27 @@ error: /** * Generate the Netplan YAML configuration for the selected netdef + * @np_state: NetplanState (as pointer), the global state to which the netdef belongs * @def: NetplanNetDefinition (as pointer), the data to be serialized * @rootdir: If not %NULL, generate configuration in this root directory * (useful for testing). */ -void -write_netplan_conf(const NetplanNetDefinition* def, const char* rootdir) +gboolean +netplan_netdef_write_yaml( + const NetplanState* np_state, + const NetplanNetDefinition* netdef, + const char* rootdir, + GError** error) { g_autofree gchar *filename = NULL; g_autofree gchar *path = NULL; /* NetworkManager produces one file per connection profile * It's 90-* to be higher priority than the default 70-netplan-set.yaml */ - if (def->backend_settings.nm.uuid) - filename = g_strconcat("90-NM-", def->backend_settings.nm.uuid, ".yaml", NULL); + if (netdef->backend_settings.nm.uuid) + filename = g_strconcat("90-NM-", netdef->backend_settings.nm.uuid, ".yaml", NULL); else - filename = g_strconcat("10-netplan-", def->id, ".yaml", NULL); + filename = g_strconcat("10-netplan-", netdef->id, ".yaml", NULL); path = g_build_path(G_DIR_SEPARATOR_S, rootdir ?: G_DIR_SEPARATOR_S, "etc", "netplan", filename, NULL); /* Start rendering YAML output */ @@ -850,10 +858,10 @@ write_netplan_conf(const NetplanNetDefinition* def, const char* rootdir) YAML_MAPPING_OPEN(event, emitter); YAML_STRING_PLAIN(event, emitter, "version", "2"); - if (netplan_def_type_name(def->type)) { - YAML_SCALAR_PLAIN(event, emitter, netplan_def_type_name(def->type)); + if (netplan_def_type_name(netdef->type)) { + YAML_SCALAR_PLAIN(event, emitter, netplan_def_type_name(netdef->type)); YAML_MAPPING_OPEN(event, emitter); - _serialize_yaml(event, emitter, def); + _serialize_yaml(np_state, event, emitter, netdef); YAML_MAPPING_CLOSE(event, emitter); } @@ -863,17 +871,18 @@ write_netplan_conf(const NetplanNetDefinition* def, const char* rootdir) /* Tear down the YAML emitter */ YAML_OUT_STOP(event, emitter); fclose(output); - return; + return TRUE; // LCOV_EXCL_START -error: - g_warning("Error generating YAML: %s", emitter->problem); +err_path: + g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Error generating YAML: %s", emitter->problem); yaml_emitter_delete(emitter); fclose(output); + return FALSE; // LCOV_EXCL_STOP } -gboolean +static gboolean contains_netdef_type(gpointer key, gpointer value, gpointer user_data) { NetplanNetDefinition *nd = value; @@ -882,96 +891,99 @@ contains_netdef_type(gpointer key, gpointer value, gpointer user_data) } /** - * Generate the Netplan YAML configuration for all currently parsed netdefs + * Generate the Netplan YAML configuration for all netdefs in the state + * @np_state: the state for which to generate the config * @file_hint: Name hint for the generated output YAML file * @rootdir: If not %NULL, generate configuration in this root directory * (useful for testing). */ -NETPLAN_INTERNAL void -write_netplan_conf_full(const char* file_hint, const char* rootdir) +NETPLAN_INTERNAL gboolean +netplan_state_write_yaml(const NetplanState* np_state, const char* file_hint, const char* rootdir, GError** error) { g_autofree gchar *path = NULL; GHashTable *ovs_ports = NULL; GHashTableIter iter; gpointer key, value; - gboolean global_values = ( (netplan_get_global_backend() != NETPLAN_BACKEND_NONE) - || has_openvswitch(&ovs_settings_global, NETPLAN_BACKEND_NONE, NULL)); + gboolean global_values = (np_state->backend != NETPLAN_BACKEND_NONE + || has_openvswitch(&np_state->ovs_settings, NETPLAN_BACKEND_NONE, NULL)); - if (global_values || (netdefs && g_hash_table_size(netdefs) > 0)) { - path = g_build_path(G_DIR_SEPARATOR_S, rootdir ?: G_DIR_SEPARATOR_S, "etc", "netplan", file_hint, NULL); + if (!global_values && netplan_state_get_netdefs_size(np_state) == 0) { + g_debug("No data/netdefs to serialize into YAML."); + return TRUE; + } - /* Start rendering YAML output */ - yaml_emitter_t emitter_data; - yaml_event_t event_data; - yaml_emitter_t* emitter = &emitter_data; - yaml_event_t* event = &event_data; - FILE *output = fopen(path, "wb"); + path = g_build_path(G_DIR_SEPARATOR_S, rootdir ?: G_DIR_SEPARATOR_S, "etc", "netplan", file_hint, NULL); - YAML_OUT_START(event, emitter, output); - /* build the netplan boilerplate YAML structure */ - YAML_SCALAR_PLAIN(event, emitter, "network"); - YAML_MAPPING_OPEN(event, emitter); - /* We support version 2 only, currently */ - YAML_STRING_PLAIN(event, emitter, "version", "2"); + /* Start rendering YAML output */ + yaml_emitter_t emitter_data; + yaml_event_t event_data; + yaml_emitter_t* emitter = &emitter_data; + yaml_event_t* event = &event_data; + FILE *output = fopen(path, "wb"); - if (netplan_get_global_backend() == NETPLAN_BACKEND_NM) { - YAML_STRING_PLAIN(event, emitter, "renderer", "NetworkManager"); - } else if (netplan_get_global_backend() == NETPLAN_BACKEND_NETWORKD) { - YAML_STRING_PLAIN(event, emitter, "renderer", "networkd"); - } + YAML_OUT_START(event, emitter, output); + /* build the netplan boilerplate YAML structure */ + YAML_SCALAR_PLAIN(event, emitter, "network"); + YAML_MAPPING_OPEN(event, emitter); + /* We support version 2 only, currently */ + YAML_STRING_PLAIN(event, emitter, "version", "2"); - /* Go through the netdefs type-by-type */ - if (netdefs && g_hash_table_size(netdefs) > 0) { - for (unsigned i = 0; i < NETPLAN_DEF_TYPE_MAX_; ++i) { - /* Per-netdef config */ - if (g_hash_table_find(netdefs, contains_netdef_type, &i)) { - if (netplan_def_type_name(i)) { - YAML_SCALAR_PLAIN(event, emitter, netplan_def_type_name(i)); - YAML_MAPPING_OPEN(event, emitter); - g_hash_table_iter_init(&iter, netdefs); - while (g_hash_table_iter_next (&iter, &key, &value)) { - NetplanNetDefinition *def = (NetplanNetDefinition *) value; - if (def->type == i) - _serialize_yaml(event, emitter, def); - } - YAML_MAPPING_CLOSE(event, emitter); - } else if (i == NETPLAN_DEF_TYPE_PORT) { - g_hash_table_iter_init(&iter, netdefs); - while (g_hash_table_iter_next (&iter, &key, &value)) { - NetplanNetDefinition *def = (NetplanNetDefinition *) value; - if (def->type == i) { - if (!ovs_ports) - ovs_ports = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - /* Insert each port:peer combination only once */ - if (!g_hash_table_lookup(ovs_ports, def->id)) - g_hash_table_insert(ovs_ports, g_strdup(def->peer), g_strdup(def->id)); - } + if (netplan_state_get_backend(np_state) == NETPLAN_BACKEND_NM) { + YAML_STRING_PLAIN(event, emitter, "renderer", "NetworkManager"); + } else if (netplan_state_get_backend(np_state) == NETPLAN_BACKEND_NETWORKD) { + YAML_STRING_PLAIN(event, emitter, "renderer", "networkd"); + } + + /* Go through the netdefs type-by-type */ + if (netplan_state_get_netdefs_size(np_state) > 0) { + for (unsigned i = 0; i < NETPLAN_DEF_TYPE_MAX_; ++i) { + /* Per-netdef config */ + if (g_hash_table_find(np_state->netdefs, contains_netdef_type, &i)) { + if (netplan_def_type_name(i)) { + YAML_SCALAR_PLAIN(event, emitter, netplan_def_type_name(i)); + YAML_MAPPING_OPEN(event, emitter); + g_hash_table_iter_init(&iter, np_state->netdefs); + while (g_hash_table_iter_next (&iter, &key, &value)) { + NetplanNetDefinition *def = (NetplanNetDefinition *) value; + if (def->type == i) + _serialize_yaml(np_state, event, emitter, def); + } + YAML_MAPPING_CLOSE(event, emitter); + } else if (i == NETPLAN_DEF_TYPE_PORT) { + g_hash_table_iter_init(&iter, np_state->netdefs); + while (g_hash_table_iter_next (&iter, &key, &value)) { + NetplanNetDefinition *def = (NetplanNetDefinition *) value; + if (def->type == i) { + if (!ovs_ports) + ovs_ports = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + /* Insert each port:peer combination only once */ + if (!g_hash_table_lookup(ovs_ports, def->id)) + g_hash_table_insert(ovs_ports, g_strdup(def->peer), g_strdup(def->id)); } } } } } + } - write_openvswitch(event, emitter, &ovs_settings_global, NETPLAN_BACKEND_NONE, ovs_ports); + write_openvswitch(event, emitter, &np_state->ovs_settings, NETPLAN_BACKEND_NONE, ovs_ports); - /* Close remaining mappings */ - YAML_MAPPING_CLOSE(event, emitter); + /* Close remaining mappings */ + YAML_MAPPING_CLOSE(event, emitter); - /* Tear down the YAML emitter */ - YAML_OUT_STOP(event, emitter); - fclose(output); - return; - - // LCOV_EXCL_START -error: - g_warning("Error generating YAML: %s", emitter->problem); - yaml_emitter_delete(emitter); - fclose(output); - // LCOV_EXCL_STOP - } else { - g_debug("No data/netdefs to serialize into YAML."); - } + /* Tear down the YAML emitter */ + YAML_OUT_STOP(event, emitter); + fclose(output); + return TRUE; + + // LCOV_EXCL_START +err_path: + g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Error generating YAML: %s", emitter->problem); + yaml_emitter_delete(emitter); + fclose(output); + return FALSE; + // LCOV_EXCL_STOP } /* XXX: implement the following functions, once needed: @@ -979,15 +991,3 @@ void write_netplan_conf_finish(const char* rootdir) void cleanup_netplan_conf(const char* rootdir) */ -/** - * Helper function for testing only - */ -NETPLAN_INTERNAL void -_write_netplan_conf(const char* netdef_id, const char* rootdir) -{ - GHashTable* ht = NULL; - const NetplanNetDefinition* def = NULL; - ht = netplan_finish_parse(NULL); - def = g_hash_table_lookup(ht, netdef_id); - write_netplan_conf(def, rootdir); -} diff --git a/src/networkd.c b/src/networkd.c index a1bc504..55c3d36 100644 --- a/src/networkd.c +++ b/src/networkd.c @@ -1149,7 +1149,8 @@ netplan_netdef_write_networkd( if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL) write_netdev_file(def, rootdir, path_base); - write_network_file(def, rootdir, path_base); + if (!netplan_netdef_write_network_file(np_state, def, rootdir, path_base, has_been_written, error)) + return FALSE; SET_OPT_OUT_PTR(has_been_written, TRUE); return TRUE; } diff --git a/src/parse-globals.h b/src/parse-globals.h index b72e0aa..e8d802f 100644 --- a/src/parse-globals.h +++ b/src/parse-globals.h @@ -20,20 +20,6 @@ #include <glib.h> #include "types.h" -/* file that is currently being processed, for useful error messages */ -extern const char* -current_file; - -/* List of "seen" ids not found in netdefs yet by the parser. - * These are removed when it exists in this list and we reach the point of - * creating a netdef for that id; so by the time we're done parsing the yaml - * document it should be empty. */ -extern GHashTable* -missing_id; - -extern int -missing_ids_found; - /* Written/updated by parse_yaml(): char* id → net_definition. * * Since both netdefs and netdefs_ordered store pointers to the same elements, @@ -49,5 +35,9 @@ netdefs_ordered; extern NetplanOVSSettings ovs_settings_global; + extern NetplanBackend global_backend; + +extern NetplanParser +global_parser; diff --git a/src/parse-nm.c b/src/parse-nm.c index 217eb54..1dc223b 100644 --- a/src/parse-nm.c +++ b/src/parse-nm.c @@ -23,6 +23,7 @@ #include "parse-nm.h" #include "parse.h" #include "util.h" +#include "types.h" #include "util-internal.h" /** @@ -429,7 +430,7 @@ read_passthrough(GKeyFile* kf, GData** list) * @filename: full path to the NetworkManager keyfile */ gboolean -netplan_parse_keyfile(const char* filename, GError** error) +netplan_parser_load_keyfile(NetplanParser* npp, const char* filename, GError** error) { g_autofree gchar *nd_id = NULL; g_autofree gchar *uuid = NULL; @@ -479,7 +480,7 @@ netplan_parse_keyfile(const char* filename, GError** error) } else nd_id = g_strconcat("NM-", uuid, NULL); g_free(tmp_str); - nd = netplan_netdef_new(nd_id, nd_type, NETPLAN_BACKEND_NM); + nd = netplan_netdef_new(npp, nd_id, nd_type, NETPLAN_BACKEND_NM); /* Handle uuid & NM name/id */ nd->backend_settings.nm.uuid = g_strdup(uuid); diff --git a/src/parse.c b/src/parse.c index ae8257b..0b2e280 100644 --- a/src/parse.c +++ b/src/parse.c @@ -28,7 +28,6 @@ #include <yaml.h> #include "parse.h" -#include "parse-globals.h" #include "names.h" #include "util-internal.h" #include "error.h" @@ -54,39 +53,9 @@ dst = g_strdup(src); \ } } -/* NetplanNetDefinition that is currently being processed */ -static NetplanNetDefinition* cur_netdef; - -/* NetplanWifiAccessPoint that is currently being processed */ -static NetplanWifiAccessPoint* cur_access_point; - -/* NetplanAuthenticationSettings that are currently being processed */ -static NetplanAuthenticationSettings* cur_auth; - -/* NetplanWireguardPeer that is currently being processed */ -static NetplanWireguardPeer* cur_wireguard_peer; - -static NetplanAddressOptions* cur_addr_option; - -static NetplanIPRoute* cur_route; -static NetplanIPRule* cur_ip_rule; - -/* Filename of the currently parsed YAML file */ -const char* cur_filename; - -static NetplanBackend backend_cur_type; - extern NetplanState global_state; -/* Set of IDs in currently parsed YAML file, for being able to detect - * "duplicate ID within one file" vs. allowing a drop-in to override/amend an - * existing definition */ -static GHashTable* ids_in_file; - -/* Global variables, defined in this file */ -int missing_ids_found; -const char* current_file; -GHashTable* missing_id; +NetplanParser global_parser = {0}; /** * Load YAML file name into a yaml_document_t. @@ -100,8 +69,6 @@ load_yaml(const char* yaml, yaml_document_t* doc, GError** error) yaml_parser_t parser; gboolean ret = TRUE; - current_file = yaml; - fyaml = g_fopen(yaml, "r"); if (!fyaml) { g_set_error(error, G_FILE_ERROR, errno, "Cannot open %s: %s", yaml, g_strerror(errno)); @@ -125,7 +92,7 @@ load_yaml(const char* yaml, yaml_document_t* doc, GError** error) * Raise a GError about a type mismatch and return FALSE. */ static gboolean -assert_type_fn(yaml_node_t* node, yaml_node_type_t expected_type, GError** error) +assert_type_fn(const NetplanParser* npp, yaml_node_t* node, yaml_node_type_t expected_type, GError** error) { if (node->type == expected_type) return TRUE; @@ -136,13 +103,13 @@ assert_type_fn(yaml_node_t* node, yaml_node_type_t expected_type, GError** error return TRUE; break; case YAML_SCALAR_NODE: - yaml_error(node, error, "expected scalar"); + yaml_error(npp, node, error, "expected scalar"); break; case YAML_SEQUENCE_NODE: - yaml_error(node, error, "expected sequence"); + yaml_error(npp, node, error, "expected sequence"); break; case YAML_MAPPING_NODE: - yaml_error(node, error, "expected mapping (check indentation)"); + yaml_error(npp, node, error, "expected mapping (check indentation)"); break; // LCOV_EXCL_START @@ -153,7 +120,7 @@ assert_type_fn(yaml_node_t* node, yaml_node_type_t expected_type, GError** error return FALSE; } -#define assert_type(n,t) { if (!assert_type_fn(n,t,error)) return FALSE; } +#define assert_type(ctx,n,t) { if (!assert_type_fn(ctx,n,t,error)) return FALSE; } static inline const char* scalar(const yaml_node_t* node) @@ -162,7 +129,7 @@ scalar(const yaml_node_t* node) } static void -add_missing_node(const yaml_node_t* node) +add_missing_node(NetplanParser *npp, const yaml_node_t* node) { NetplanMissingNode* missing; @@ -171,23 +138,23 @@ add_missing_node(const yaml_node_t* node) * seen by the compiler). We can use it later to write an sensible error * message and point the user in the right direction. */ missing = g_new0(NetplanMissingNode, 1); - missing->netdef_id = cur_netdef->id; + missing->netdef_id = npp->current.netdef->id; missing->node = node; g_debug("recording missing yaml_node_t %s", scalar(node)); - g_hash_table_insert(missing_id, (gpointer)scalar(node), missing); + g_hash_table_insert(npp->missing_id, (gpointer)scalar(node), missing); } /** * Check that node contains a valid ID/interface name. Raise GError if not. */ static gboolean -assert_valid_id(yaml_node_t* node, GError** error) +assert_valid_id(const NetplanParser* npp, yaml_node_t* node, GError** error) { static regex_t re; static gboolean re_inited = FALSE; - assert_type(node, YAML_SCALAR_NODE); + assert_type(npp, node, YAML_SCALAR_NODE); if (!re_inited) { g_assert(regcomp(&re, "^[[:alnum:][:punct:]]+$", REG_EXTENDED|REG_NOSUB) == 0); @@ -195,28 +162,22 @@ assert_valid_id(yaml_node_t* node, GError** error) } if (regexec(&re, scalar(node), 0, NULL, 0) != 0) - return yaml_error(node, error, "Invalid name '%s'", scalar(node)); + return yaml_error(npp, node, error, "Invalid name '%s'", scalar(node)); return TRUE; } NetplanNetDefinition* -netplan_netdef_new(const char* id, NetplanDefType type, NetplanBackend backend) +netplan_netdef_new(NetplanParser *npp, const char* id, NetplanDefType type, NetplanBackend backend) { /* create new network definition */ NetplanNetDefinition *netdef = g_new0(NetplanNetDefinition, 1); reset_netdef(netdef, type, backend); netdef->id = g_strdup(id); - if (!netdefs) - netdefs = g_hash_table_new(g_str_hash, g_str_equal); - g_hash_table_insert(netdefs, netdef->id, netdef); - /* netdefs_ordered now owns the allocated object */ - netdefs_ordered = g_list_append(netdefs_ordered, netdef); - - /* Update the convenience pointer cur_netdef. This is a weak ref, - * the previous value is owned by netdefs_ordered */ - cur_netdef = netdef; - + if (!npp->parsed_defs) + npp->parsed_defs = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(npp->parsed_defs, netdef->id, netdef); + npp->ordered = g_list_append(npp->ordered, netdef); return netdef; } @@ -224,7 +185,7 @@ netplan_netdef_new(const char* id, NetplanDefType type, NetplanBackend backend) * Data types and functions for interpreting YAML nodes ****************************************************/ -typedef gboolean (*node_handler) (yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error); +typedef gboolean (*node_handler) (NetplanParser* npp, yaml_node_t* node, const void* data, GError** error); typedef struct mapping_entry_handler_s { /* mapping key (must be scalar) */ @@ -263,11 +224,11 @@ get_handler(const mapping_entry_handler* handlers, const char* key) * Returns: TRUE on success, FALSE on error (@error gets set then). */ static gboolean -process_mapping(yaml_document_t* doc, yaml_node_t* node, const mapping_entry_handler* handlers, GList** out_values, GError** error) +process_mapping(NetplanParser* npp, yaml_node_t* node, const mapping_entry_handler* handlers, GList** out_values, GError** error) { yaml_node_pair_t* entry; - assert_type(node, YAML_MAPPING_NODE); + assert_type(npp, node, YAML_MAPPING_NODE); for (entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) { yaml_node_t* key, *value; @@ -275,22 +236,22 @@ process_mapping(yaml_document_t* doc, yaml_node_t* node, const mapping_entry_han g_assert(error == NULL || *error == NULL); - key = yaml_document_get_node(doc, entry->key); - value = yaml_document_get_node(doc, entry->value); - assert_type(key, YAML_SCALAR_NODE); + key = yaml_document_get_node(&npp->doc, entry->key); + value = yaml_document_get_node(&npp->doc, entry->value); + assert_type(npp, key, YAML_SCALAR_NODE); h = get_handler(handlers, scalar(key)); if (!h) - return yaml_error(key, error, "unknown key '%s'", scalar(key)); - assert_type(value, h->type); + return yaml_error(npp, key, error, "unknown key '%s'", scalar(key)); + assert_type(npp, value, h->type); if (out_values) *out_values = g_list_prepend(*out_values, g_strdup(scalar(key))); if (h->map_handlers) { g_assert(h->handler == NULL); g_assert(h->type == YAML_MAPPING_NODE); - if (!process_mapping(doc, value, h->map_handlers, NULL, error)) + if (!process_mapping(npp, value, h->map_handlers, NULL, error)) return FALSE; } else { - if (!h->handler(doc, value, h->data, error)) + if (!h->handler(npp, value, h->data, error)) return FALSE; } } @@ -308,7 +269,7 @@ process_mapping(yaml_document_t* doc, yaml_node_t* node, const mapping_entry_han * @data: offset into entryptr struct where the guint field to write is located */ static gboolean -handle_generic_guint(yaml_document_t* doc, yaml_node_t* node, const void* entryptr, const void* data, GError** error) +handle_generic_guint(NetplanParser* npp, yaml_node_t* node, const void* entryptr, const void* data, GError** error) { g_assert(entryptr); guint offset = GPOINTER_TO_UINT(data); @@ -317,7 +278,7 @@ handle_generic_guint(yaml_document_t* doc, yaml_node_t* node, const void* entryp v = g_ascii_strtoull(scalar(node), &endptr, 10); if (*endptr != '\0' || v > G_MAXUINT) - return yaml_error(node, error, "invalid unsigned int value '%s'", scalar(node)); + return yaml_error(npp, node, error, "invalid unsigned int value '%s'", scalar(node)); *((guint*) ((void*) entryptr + offset)) = (guint) v; return TRUE; @@ -330,7 +291,7 @@ handle_generic_guint(yaml_document_t* doc, yaml_node_t* node, const void* entryp * located */ static gboolean -handle_generic_str(yaml_document_t* doc, yaml_node_t* node, void* entryptr, const void* data, GError** error) +handle_generic_str(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error) { g_assert(entryptr); guint offset = GPOINTER_TO_UINT(data); @@ -347,7 +308,7 @@ handle_generic_str(yaml_document_t* doc, yaml_node_t* node, void* entryptr, cons * located */ static gboolean -handle_generic_mac(yaml_document_t* doc, yaml_node_t* node, void* entryptr, const void* data, GError** error) +handle_generic_mac(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error) { g_assert(entryptr); static regex_t re; @@ -361,9 +322,9 @@ handle_generic_mac(yaml_document_t* doc, yaml_node_t* node, void* entryptr, cons } if (regexec(&re, scalar(node), 0, NULL, 0) != 0) - return yaml_error(node, error, "Invalid MAC address '%s', must be XX:XX:XX:XX:XX:XX", scalar(node)); + return yaml_error(npp, node, error, "Invalid MAC address '%s', must be XX:XX:XX:XX:XX:XX", scalar(node)); - return handle_generic_str(doc, node, entryptr, data, error); + return handle_generic_str(npp, node, entryptr, data, error); } /* @@ -372,7 +333,7 @@ handle_generic_mac(yaml_document_t* doc, yaml_node_t* node, void* entryptr, cons * @data: offset into entryptr struct where the boolean field to write is located */ static gboolean -handle_generic_bool(yaml_document_t* doc, yaml_node_t* node, void* entryptr, const void* data, GError** error) +handle_generic_bool(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error) { g_assert(entryptr); guint offset = GPOINTER_TO_UINT(data); @@ -389,7 +350,7 @@ handle_generic_bool(yaml_document_t* doc, yaml_node_t* node, void* entryptr, con g_ascii_strcasecmp(scalar(node), "n") == 0) v = FALSE; else - return yaml_error(node, error, "invalid boolean value '%s'", scalar(node)); + return yaml_error(npp, node, error, "invalid boolean value '%s'", scalar(node)); *((gboolean*) ((void*) entryptr + offset)) = v; return TRUE; @@ -401,7 +362,7 @@ handle_generic_bool(yaml_document_t* doc, yaml_node_t* node, void* entryptr, con * @data: offset into entryptr struct where the boolean field to write is located */ static gboolean -handle_generic_map(yaml_document_t* doc, yaml_node_t* node, void* entryptr, const void* data, GError** error) +handle_generic_map(NetplanParser *npp, yaml_node_t* node, void* entryptr, const void* data, GError** error) { guint offset = GPOINTER_TO_UINT(data); GHashTable** map = (GHashTable**) ((void*) entryptr + offset); @@ -411,15 +372,15 @@ handle_generic_map(yaml_document_t* doc, yaml_node_t* node, void* entryptr, cons for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) { yaml_node_t* key, *value; - key = yaml_document_get_node(doc, entry->key); - value = yaml_document_get_node(doc, entry->value); + key = yaml_document_get_node(&npp->doc, entry->key); + value = yaml_document_get_node(&npp->doc, entry->value); - assert_type(key, YAML_SCALAR_NODE); - assert_type(value, YAML_SCALAR_NODE); + assert_type(npp, key, YAML_SCALAR_NODE); + assert_type(npp, value, YAML_SCALAR_NODE); /* TODO: make sure we free all the memory here */ if (!g_hash_table_insert(*map, g_strdup(scalar(key)), g_strdup(scalar(value)))) - return yaml_error(node, error, "duplicate map entry '%s'", scalar(key)); + return yaml_error(npp, node, error, "duplicate map entry '%s'", scalar(key)); } return TRUE; @@ -431,7 +392,7 @@ handle_generic_map(yaml_document_t* doc, yaml_node_t* node, void* entryptr, cons * @data: offset into entryptr struct where the boolean field to write is located */ static gboolean -handle_generic_datalist(yaml_document_t* doc, yaml_node_t* node, void* entryptr, const void* data, GError** error) +handle_generic_datalist(NetplanParser *npp, yaml_node_t* node, void* entryptr, const void* data, GError** error) { guint offset = GPOINTER_TO_UINT(data); GData** list = (GData**) ((void*) entryptr + offset); @@ -441,11 +402,11 @@ handle_generic_datalist(yaml_document_t* doc, yaml_node_t* node, void* entryptr, for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) { yaml_node_t* key, *value; - key = yaml_document_get_node(doc, entry->key); - value = yaml_document_get_node(doc, entry->value); + key = yaml_document_get_node(&npp->doc, entry->key); + value = yaml_document_get_node(&npp->doc, entry->value); - assert_type(key, YAML_SCALAR_NODE); - assert_type(value, YAML_SCALAR_NODE); + assert_type(npp, key, YAML_SCALAR_NODE); + assert_type(npp, value, YAML_SCALAR_NODE); g_datalist_set_data_full(list, g_strdup(scalar(key)), g_strdup(scalar(value)), g_free); } @@ -454,31 +415,31 @@ handle_generic_datalist(yaml_document_t* doc, yaml_node_t* node, void* entryptr, } /** - * Generic handler for setting a cur_netdef string field from a scalar node + * Generic handler for setting a npp->current.netdef string field from a scalar node * @data: offset into NetplanNetDefinition where the const char* field to write is * located */ static gboolean -handle_netdef_str(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_netdef_str(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - return handle_generic_str(doc, node, cur_netdef, data, error); + return handle_generic_str(npp, node, npp->current.netdef, data, error); } /** - * Generic handler for setting a cur_netdef ID/iface name field from a scalar node + * Generic handler for setting a npp->current.netdef ID/iface name field from a scalar node * @data: offset into NetplanNetDefinition where the const char* field to write is * located */ static gboolean -handle_netdef_id(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_netdef_id(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - if (!assert_valid_id(node, error)) + if (!assert_valid_id(npp, node, error)) return FALSE; - return handle_netdef_str(doc, node, data, error); + return handle_netdef_str(npp, node, data, error); } /** - * Generic handler for setting a cur_netdef ID/iface name field referring to an + * Generic handler for setting a npp->current.netdef ID/iface name field referring to an * existing ID from a scalar node. This handler also includes a special case * handler for OVS VLANs, switching the backend implicitly to OVS for such * interfaces @@ -486,20 +447,21 @@ handle_netdef_id(yaml_document_t* doc, yaml_node_t* node, const void* data, GErr * located */ static gboolean -handle_netdef_id_ref(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_netdef_id_ref(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { guint offset = GPOINTER_TO_UINT(data); NetplanNetDefinition* ref = NULL; - ref = g_hash_table_lookup(netdefs, scalar(node)); + ref = g_hash_table_lookup(npp->parsed_defs, scalar(node)); if (!ref) { - add_missing_node(node); + add_missing_node(npp, node); } else { - *((NetplanNetDefinition**) ((void*) cur_netdef + offset)) = ref; + NetplanNetDefinition* netdef = npp->current.netdef; + *((NetplanNetDefinition**) ((void*) netdef + offset)) = ref; - if (cur_netdef->type == NETPLAN_DEF_TYPE_VLAN && ref->backend == NETPLAN_BACKEND_OVS) { - g_debug("%s: VLAN defined for openvswitch interface, choosing OVS backend", cur_netdef->id); - cur_netdef->backend = NETPLAN_BACKEND_OVS; + if (netdef->type == NETPLAN_DEF_TYPE_VLAN && ref->backend == NETPLAN_BACKEND_OVS) { + g_debug("%s: VLAN defined for openvswitch interface, choosing OVS backend", netdef->id); + netdef->backend = NETPLAN_BACKEND_OVS; } } return TRUE; @@ -507,41 +469,41 @@ handle_netdef_id_ref(yaml_document_t* doc, yaml_node_t* node, const void* data, /** - * Generic handler for setting a cur_netdef MAC address field from a scalar node + * Generic handler for setting a npp->current.netdef MAC address field from a scalar node * @data: offset into NetplanNetDefinition where the const char* field to write is * located */ static gboolean -handle_netdef_mac(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_netdef_mac(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - return handle_generic_mac(doc, node, cur_netdef, data, error); + return handle_generic_mac(npp, node, npp->current.netdef, data, error); } /** - * Generic handler for setting a cur_netdef gboolean field from a scalar node + * Generic handler for setting a npp->current.netdef gboolean field from a scalar node * @data: offset into NetplanNetDefinition where the gboolean field to write is located */ static gboolean -handle_netdef_bool(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_netdef_bool(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - return handle_generic_bool(doc, node, cur_netdef, data, error); + return handle_generic_bool(npp, node, npp->current.netdef, data, error); } /** - * Generic handler for setting a cur_netdef guint field from a scalar node + * Generic handler for setting a npp->current.netdef guint field from a scalar node * @data: offset into NetplanNetDefinition where the guint field to write is located */ static gboolean -handle_netdef_guint(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_netdef_guint(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - return handle_generic_guint(doc, node, cur_netdef, data, error); + return handle_generic_guint(npp, node, npp->current.netdef, data, error); } static gboolean -handle_netdef_ip4(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_netdef_ip4(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { guint offset = GPOINTER_TO_UINT(data); - char** dest = (char**) ((void*) cur_netdef + offset); + char** dest = (char**) ((void*) npp->current.netdef + offset); g_autofree char* addr = NULL; char* prefix_len; @@ -552,12 +514,12 @@ handle_netdef_ip4(yaml_document_t* doc, yaml_node_t* node, const void* data, GEr /* FIXME: stop excluding this from coverage; refactor address handling instead */ // LCOV_EXCL_START if (prefix_len) - return yaml_error(node, error, + return yaml_error(npp, node, error, "invalid address: a single IPv4 address (without /prefixlength) is required"); /* is it an IPv4 address? */ if (!is_ip4_address(addr)) - return yaml_error(node, error, + return yaml_error(npp, node, error, "invalid IPv4 address: %s", scalar(node)); // LCOV_EXCL_STOP @@ -568,10 +530,10 @@ handle_netdef_ip4(yaml_document_t* doc, yaml_node_t* node, const void* data, GEr } static gboolean -handle_netdef_ip6(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_netdef_ip6(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { guint offset = GPOINTER_TO_UINT(data); - char** dest = (char**) ((void*) cur_netdef + offset); + char** dest = (char**) ((void*) npp->current.netdef + offset); g_autofree char* addr = NULL; char* prefix_len; @@ -582,12 +544,12 @@ handle_netdef_ip6(yaml_document_t* doc, yaml_node_t* node, const void* data, GEr /* FIXME: stop excluding this from coverage; refactor address handling instead */ // LCOV_EXCL_START if (prefix_len) - return yaml_error(node, error, + return yaml_error(npp, node, error, "invalid address: a single IPv6 address (without /prefixlength) is required"); /* is it an IPv6 address? */ if (!is_ip6_address(addr)) - return yaml_error(node, error, + return yaml_error(npp, node, error, "invalid IPv6 address: %s", scalar(node)); // LCOV_EXCL_STOP @@ -598,40 +560,40 @@ handle_netdef_ip6(yaml_document_t* doc, yaml_node_t* node, const void* data, GEr } static gboolean -handle_netdef_addrgen(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_netdef_addrgen(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - g_assert(cur_netdef); + g_assert(npp->current.netdef); if (strcmp(scalar(node), "eui64") == 0) - cur_netdef->ip6_addr_gen_mode = NETPLAN_ADDRGEN_EUI64; + npp->current.netdef->ip6_addr_gen_mode = NETPLAN_ADDRGEN_EUI64; else if (strcmp(scalar(node), "stable-privacy") == 0) - cur_netdef->ip6_addr_gen_mode = NETPLAN_ADDRGEN_STABLEPRIVACY; + npp->current.netdef->ip6_addr_gen_mode = NETPLAN_ADDRGEN_STABLEPRIVACY; else - return yaml_error(node, error, "unknown ipv6-address-generation '%s'", scalar(node)); + return yaml_error(npp, node, error, "unknown ipv6-address-generation '%s'", scalar(node)); return TRUE; } static gboolean -handle_netdef_addrtok(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_netdef_addrtok(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - g_assert(cur_netdef); - gboolean ret = handle_netdef_str(doc, node, data, error); - if (!is_ip6_address(cur_netdef->ip6_addr_gen_token)) - return yaml_error(node, error, "invalid ipv6-address-token '%s'", scalar(node)); + g_assert(npp->current.netdef); + gboolean ret = handle_netdef_str(npp, node, data, error); + if (!is_ip6_address(npp->current.netdef->ip6_addr_gen_token)) + return yaml_error(npp, node, error, "invalid ipv6-address-token '%s'", scalar(node)); return ret; } static gboolean -handle_netdef_map(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_netdef_map(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - g_assert(cur_netdef); - return handle_generic_map(doc, node, cur_netdef, data, error); + g_assert(npp->current.netdef); + return handle_generic_map(npp, node, npp->current.netdef, data, error); } static gboolean -handle_netdef_datalist(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_netdef_datalist(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - g_assert(cur_netdef); - return handle_generic_datalist(doc, node, cur_netdef, data, error); + g_assert(npp->current.netdef); + return handle_generic_datalist(npp, node, npp->current.netdef, data, error); } /**************************************************** @@ -650,45 +612,47 @@ static const mapping_entry_handler match_handlers[] = { ****************************************************/ static gboolean -handle_auth_str(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_auth_str(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - g_assert(cur_auth); + g_assert(npp->current.auth); guint offset = GPOINTER_TO_UINT(data); - char** dest = (char**) ((void*) cur_auth + offset); + char** dest = (char**) ((void*) npp->current.auth + offset); g_free(*dest); *dest = g_strdup(scalar(node)); return TRUE; } static gboolean -handle_auth_key_management(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_auth_key_management(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - g_assert(cur_auth); + NetplanAuthenticationSettings* auth = npp->current.auth; + g_assert(auth); if (strcmp(scalar(node), "none") == 0) - cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_NONE; + auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_NONE; else if (strcmp(scalar(node), "psk") == 0) - cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK; + auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK; else if (strcmp(scalar(node), "eap") == 0) - cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP; + auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP; else if (strcmp(scalar(node), "802.1x") == 0) - cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_8021X; + auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_8021X; else - return yaml_error(node, error, "unknown key management type '%s'", scalar(node)); + return yaml_error(npp, node, error, "unknown key management type '%s'", scalar(node)); return TRUE; } static gboolean -handle_auth_method(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_auth_method(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - g_assert(cur_auth); + NetplanAuthenticationSettings* auth = npp->current.auth; + g_assert(auth); if (strcmp(scalar(node), "tls") == 0) - cur_auth->eap_method = NETPLAN_AUTH_EAP_TLS; + auth->eap_method = NETPLAN_AUTH_EAP_TLS; else if (strcmp(scalar(node), "peap") == 0) - cur_auth->eap_method = NETPLAN_AUTH_EAP_PEAP; + auth->eap_method = NETPLAN_AUTH_EAP_PEAP; else if (strcmp(scalar(node), "ttls") == 0) - cur_auth->eap_method = NETPLAN_AUTH_EAP_TTLS; + auth->eap_method = NETPLAN_AUTH_EAP_TTLS; else - return yaml_error(node, error, "unknown EAP method '%s'", scalar(node)); + return yaml_error(npp, node, error, "unknown EAP method '%s'", scalar(node)); return TRUE; } @@ -711,11 +675,10 @@ static const mapping_entry_handler auth_handlers[] = { ****************************************************/ static NetplanBackend -get_default_backend_for_type(NetplanDefType type) +get_default_backend_for_type(const NetplanParser *npp, NetplanDefType type) { - NetplanBackend backend = netplan_state_get_backend(&global_state); - if (backend != NETPLAN_BACKEND_NONE) - return backend; + if (npp->global_backend != NETPLAN_BACKEND_NONE) + return npp->global_backend; /* networkd can handle all device types at the moment, so nothing * type-specific */ @@ -723,88 +686,92 @@ get_default_backend_for_type(NetplanDefType type) } static gboolean -handle_access_point_str(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_access_point_str(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - return handle_generic_str(doc, node, cur_access_point, data, error); + return handle_generic_str(npp, node, npp->current.access_point, data, error); } static gboolean -handle_access_point_datalist(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_access_point_datalist(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - g_assert(cur_access_point); - return handle_generic_datalist(doc, node, cur_access_point, data, error); + g_assert(npp->current.access_point); + return handle_generic_datalist(npp, node, npp->current.access_point, data, error); } static gboolean -handle_access_point_guint(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_access_point_guint(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - return handle_generic_guint(doc, node, cur_access_point, data, error); + return handle_generic_guint(npp, node, npp->current.access_point, data, error); } static gboolean -handle_access_point_mac(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_access_point_mac(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - return handle_generic_mac(doc, node, cur_access_point, data, error); + return handle_generic_mac(npp, node, npp->current.access_point, data, error); } static gboolean -handle_access_point_bool(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_access_point_bool(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - return handle_generic_bool(doc, node, cur_access_point, data, error); + return handle_generic_bool(npp, node, npp->current.access_point, data, error); } static gboolean -handle_access_point_password(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_access_point_password(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - g_assert(cur_access_point); + NetplanWifiAccessPoint *access_point = npp->current.access_point; + g_assert(access_point); /* shortcut for WPA-PSK */ - cur_access_point->has_auth = TRUE; - cur_access_point->auth.key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK; - g_free(cur_access_point->auth.password); - cur_access_point->auth.password = g_strdup(scalar(node)); + access_point->has_auth = TRUE; + access_point->auth.key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK; + g_free(access_point->auth.password); + access_point->auth.password = g_strdup(scalar(node)); return TRUE; } static gboolean -handle_access_point_auth(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_access_point_auth(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { + NetplanWifiAccessPoint *access_point = npp->current.access_point; gboolean ret; - g_assert(cur_access_point); - cur_access_point->has_auth = TRUE; + g_assert(access_point); + access_point->has_auth = TRUE; - cur_auth = &cur_access_point->auth; - ret = process_mapping(doc, node, auth_handlers, NULL, error); - cur_auth = NULL; + npp->current.auth = &access_point->auth; + ret = process_mapping(npp, node, auth_handlers, NULL, error); + npp->current.auth = NULL; return ret; } static gboolean -handle_access_point_mode(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_access_point_mode(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - g_assert(cur_access_point); + NetplanWifiAccessPoint *access_point = npp->current.access_point; + g_assert(access_point); if (strcmp(scalar(node), "infrastructure") == 0) - cur_access_point->mode = NETPLAN_WIFI_MODE_INFRASTRUCTURE; + access_point->mode = NETPLAN_WIFI_MODE_INFRASTRUCTURE; else if (strcmp(scalar(node), "adhoc") == 0) - cur_access_point->mode = NETPLAN_WIFI_MODE_ADHOC; + access_point->mode = NETPLAN_WIFI_MODE_ADHOC; else if (strcmp(scalar(node), "ap") == 0) - cur_access_point->mode = NETPLAN_WIFI_MODE_AP; + access_point->mode = NETPLAN_WIFI_MODE_AP; else - return yaml_error(node, error, "unknown wifi mode '%s'", scalar(node)); + return yaml_error(npp, node, error, "unknown wifi mode '%s'", scalar(node)); return TRUE; } static gboolean -handle_access_point_band(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_access_point_band(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - g_assert(cur_access_point); + NetplanWifiAccessPoint *access_point = npp->current.access_point; + g_assert(access_point); if (strcmp(scalar(node), "5GHz") == 0 || strcmp(scalar(node), "5G") == 0) - cur_access_point->band = NETPLAN_WIFI_BAND_5; + access_point->band = NETPLAN_WIFI_BAND_5; else if (strcmp(scalar(node), "2.4GHz") == 0 || strcmp(scalar(node), "2.4G") == 0) - cur_access_point->band = NETPLAN_WIFI_BAND_24; + access_point->band = NETPLAN_WIFI_BAND_24; else - return yaml_error(node, error, "unknown wifi band '%s'", scalar(node)); + return yaml_error(npp, node, error, "unknown wifi band '%s'", scalar(node)); return TRUE; } @@ -847,55 +814,55 @@ static const mapping_entry_handler wifi_access_point_handlers[] = { * Parse scalar node's string into a netdef_backend. */ static gboolean -parse_renderer(yaml_node_t* node, NetplanBackend* backend, GError** error) +parse_renderer(NetplanParser* npp, yaml_node_t* node, NetplanBackend* backend, GError** error) { if (strcmp(scalar(node), "networkd") == 0) *backend = NETPLAN_BACKEND_NETWORKD; else if (strcmp(scalar(node), "NetworkManager") == 0) *backend = NETPLAN_BACKEND_NM; else - return yaml_error(node, error, "unknown renderer '%s'", scalar(node)); + return yaml_error(npp, node, error, "unknown renderer '%s'", scalar(node)); return TRUE; } static gboolean -handle_netdef_renderer(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_netdef_renderer(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - if (cur_netdef->type == NETPLAN_DEF_TYPE_VLAN) { + if (npp->current.netdef->type == NETPLAN_DEF_TYPE_VLAN) { if (strcmp(scalar(node), "sriov") == 0) { - cur_netdef->sriov_vlan_filter = TRUE; + npp->current.netdef->sriov_vlan_filter = TRUE; return TRUE; } } - return parse_renderer(node, &cur_netdef->backend, error); + return parse_renderer(npp, node, &npp->current.netdef->backend, error); } static gboolean -handle_accept_ra(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_accept_ra(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - gboolean ret = handle_generic_bool(doc, node, cur_netdef, data, error); - if (cur_netdef->accept_ra) - cur_netdef->accept_ra = NETPLAN_RA_MODE_ENABLED; + gboolean ret = handle_generic_bool(npp, node, npp->current.netdef, data, error); + if (npp->current.netdef->accept_ra) + npp->current.netdef->accept_ra = NETPLAN_RA_MODE_ENABLED; else - cur_netdef->accept_ra = NETPLAN_RA_MODE_DISABLED; + npp->current.netdef->accept_ra = NETPLAN_RA_MODE_DISABLED; return ret; } static gboolean -handle_activation_mode(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_activation_mode(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { if (g_strcmp0(scalar(node), "manual") && g_strcmp0(scalar(node), "off")) - return yaml_error(node, error, "Value of 'activation-mode' needs to be 'manual' or 'off'"); + return yaml_error(npp, node, error, "Value of 'activation-mode' needs to be 'manual' or 'off'"); - return handle_netdef_str(doc, node, data, error); + return handle_netdef_str(npp, node, data, error); } static gboolean -handle_match(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_match(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - cur_netdef->has_match = TRUE; - return process_mapping(doc, node, match_handlers, NULL, error); + npp->current.netdef->has_match = TRUE; + return process_mapping(npp, node, match_handlers, NULL, error); } NETPLAN_ABI struct NetplanWifiWowlanType @@ -913,56 +880,56 @@ NETPLAN_WIFI_WOWLAN_TYPES[] = { }; static gboolean -handle_wowlan(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_wowlan(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { - yaml_node_t *entry = yaml_document_get_node(doc, *i); - assert_type(entry, YAML_SCALAR_NODE); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); + assert_type(npp, entry, YAML_SCALAR_NODE); int found = FALSE; for (unsigned i = 0; NETPLAN_WIFI_WOWLAN_TYPES[i].name != NULL; ++i) { if (g_ascii_strcasecmp(scalar(entry), NETPLAN_WIFI_WOWLAN_TYPES[i].name) == 0) { - cur_netdef->wowlan |= NETPLAN_WIFI_WOWLAN_TYPES[i].flag; + npp->current.netdef->wowlan |= NETPLAN_WIFI_WOWLAN_TYPES[i].flag; found = TRUE; break; } } if (!found) - return yaml_error(node, error, "invalid value for wakeonwlan: '%s'", scalar(entry)); + return yaml_error(npp, node, error, "invalid value for wakeonwlan: '%s'", scalar(entry)); } - if (cur_netdef->wowlan > NETPLAN_WIFI_WOWLAN_DEFAULT && cur_netdef->wowlan & NETPLAN_WIFI_WOWLAN_TYPES[0].flag) - return yaml_error(node, error, "'default' is an exclusive flag for wakeonwlan"); + if (npp->current.netdef->wowlan > NETPLAN_WIFI_WOWLAN_DEFAULT && npp->current.netdef->wowlan & NETPLAN_WIFI_WOWLAN_TYPES[0].flag) + return yaml_error(npp, node, error, "'default' is an exclusive flag for wakeonwlan"); return TRUE; } static gboolean -handle_auth(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_auth(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { gboolean ret; - cur_netdef->has_auth = TRUE; + npp->current.netdef->has_auth = TRUE; - cur_auth = &cur_netdef->auth; - ret = process_mapping(doc, node, auth_handlers, NULL, error); - cur_auth = NULL; + npp->current.auth = &npp->current.netdef->auth; + ret = process_mapping(npp, node, auth_handlers, NULL, error); + npp->current.auth = NULL; return ret; } static gboolean -handle_address_option_lifetime(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_address_option_lifetime(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { if (g_ascii_strcasecmp(scalar(node), "0") != 0 && g_ascii_strcasecmp(scalar(node), "forever") != 0) { - return yaml_error(node, error, "invalid lifetime value '%s'", scalar(node)); + return yaml_error(npp, node, error, "invalid lifetime value '%s'", scalar(node)); } - return handle_generic_str(doc, node, cur_addr_option, data, error); + return handle_generic_str(npp, node, npp->current.addr_options, data, error); } static gboolean -handle_address_option_label(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_address_option_label(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - return handle_generic_str(doc, node, cur_addr_option, data, error); + return handle_generic_str(npp, node, npp->current.addr_options, data, error); } const mapping_entry_handler address_option_handlers[] = { @@ -977,7 +944,7 @@ const mapping_entry_handler address_option_handlers[] = { * @data: offset into entryptr struct where the array to write is located */ static gboolean -handle_generic_addresses(yaml_document_t* doc, yaml_node_t* node, gboolean check_zero_prefix, GArray** ip4, GArray** ip6, GError** error) +handle_generic_addresses(NetplanParser* npp, yaml_node_t* node, gboolean check_zero_prefix, GArray** ip4, GArray** ip6, GError** error) { g_assert(ip4); g_assert(ip6); @@ -985,58 +952,60 @@ handle_generic_addresses(yaml_document_t* doc, yaml_node_t* node, gboolean check g_autofree char* addr = NULL; char* prefix_len; guint64 prefix_len_num; - yaml_node_t *entry = yaml_document_get_node(doc, *i); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); yaml_node_t *key = NULL; yaml_node_t *value = NULL; if (entry->type != YAML_SCALAR_NODE && entry->type != YAML_MAPPING_NODE) { - return yaml_error(entry, error, "expected either scalar or mapping (check indentation)"); + return yaml_error(npp, entry, error, "expected either scalar or mapping (check indentation)"); } if (entry->type == YAML_MAPPING_NODE) { - key = yaml_document_get_node(doc, entry->data.mapping.pairs.start->key); - value = yaml_document_get_node(doc, entry->data.mapping.pairs.start->value); + key = yaml_document_get_node(&npp->doc, entry->data.mapping.pairs.start->key); + value = yaml_document_get_node(&npp->doc, entry->data.mapping.pairs.start->value); entry = key; } - assert_type(entry, YAML_SCALAR_NODE); + assert_type(npp, entry, YAML_SCALAR_NODE); /* split off /prefix_len */ addr = g_strdup(scalar(entry)); prefix_len = strrchr(addr, '/'); if (!prefix_len) - return yaml_error(node, error, "address '%s' is missing /prefixlength", scalar(entry)); + return yaml_error(npp, node, error, "address '%s' is missing /prefixlength", scalar(entry)); *prefix_len = '\0'; prefix_len++; /* skip former '/' into first char of prefix */ prefix_len_num = g_ascii_strtoull(prefix_len, NULL, 10); if (value) { if (!is_ip4_address(addr) && !is_ip6_address(addr)) - return yaml_error(node, error, "malformed address '%s', must be X.X.X.X/NN or X:X:X:X:X:X:X:X/NN", scalar(entry)); + return yaml_error(npp, node, error, "malformed address '%s', must be X.X.X.X/NN or X:X:X:X:X:X:X:X/NN", scalar(entry)); - if (!cur_netdef->address_options) - cur_netdef->address_options = g_array_new(FALSE, FALSE, sizeof(NetplanAddressOptions*)); + if (!npp->current.netdef->address_options) + npp->current.netdef->address_options = g_array_new(FALSE, FALSE, sizeof(NetplanAddressOptions*)); - for (unsigned i = 0; i < cur_netdef->address_options->len; ++i) { - NetplanAddressOptions* opts = g_array_index(cur_netdef->address_options, NetplanAddressOptions*, i); + for (unsigned i = 0; i < npp->current.netdef->address_options->len; ++i) { + NetplanAddressOptions* opts = g_array_index(npp->current.netdef->address_options, NetplanAddressOptions*, i); /* check for multi-pass parsing, return early if options for this address already exist */ if (!g_strcmp0(scalar(key), opts->address)) return TRUE; } - cur_addr_option = g_new0(NetplanAddressOptions, 1); - cur_addr_option->address = g_strdup(scalar(key)); + npp->current.addr_options = g_new0(NetplanAddressOptions, 1); + npp->current.addr_options->address = g_strdup(scalar(key)); - if (!process_mapping(doc, value, address_option_handlers, NULL, error)) + if (!process_mapping(npp, value, address_option_handlers, NULL, error)) return FALSE; - g_array_append_val(cur_netdef->address_options, cur_addr_option); + g_array_append_val(npp->current.netdef->address_options, npp->current.addr_options); + npp->current.addr_options = NULL; + continue; } /* is it an IPv4 address? */ if (is_ip4_address(addr)) { if ((check_zero_prefix && prefix_len_num == 0) || prefix_len_num > 32) - return yaml_error(node, error, "invalid prefix length in address '%s'", scalar(entry)); + return yaml_error(npp, node, error, "invalid prefix length in address '%s'", scalar(entry)); if (!*ip4) *ip4 = g_array_new(FALSE, FALSE, sizeof(char*)); @@ -1054,7 +1023,7 @@ skip_ip4: /* is it an IPv6 address? */ if (is_ip6_address(addr)) { if ((check_zero_prefix && prefix_len_num == 0) || prefix_len_num > 128) - return yaml_error(node, error, "invalid prefix length in address '%s'", scalar(entry)); + return yaml_error(npp, node, error, "invalid prefix length in address '%s'", scalar(entry)); if (!*ip6) *ip6 = g_array_new(FALSE, FALSE, sizeof(char*)); @@ -1068,106 +1037,106 @@ skip_ip6: continue; } - return yaml_error(node, error, "malformed address '%s', must be X.X.X.X/NN or X:X:X:X:X:X:X:X/NN", scalar(entry)); + return yaml_error(npp, node, error, "malformed address '%s', must be X.X.X.X/NN or X:X:X:X:X:X:X:X/NN", scalar(entry)); } return TRUE; } static gboolean -handle_addresses(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_addresses(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - return handle_generic_addresses(doc, node, TRUE, &(cur_netdef->ip4_addresses), &(cur_netdef->ip6_addresses), error); + return handle_generic_addresses(npp, node, TRUE, &(npp->current.netdef->ip4_addresses), &(npp->current.netdef->ip6_addresses), error); } static gboolean -handle_gateway4(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_gateway4(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { if (!is_ip4_address(scalar(node))) - return yaml_error(node, error, "invalid IPv4 address '%s'", scalar(node)); - set_str_if_null(cur_netdef->gateway4, scalar(node)); + return yaml_error(npp, node, error, "invalid IPv4 address '%s'", scalar(node)); + set_str_if_null(npp->current.netdef->gateway4, scalar(node)); g_warning("`gateway4` has been deprecated, use default routes instead.\n" "See the 'Default routes' section of the documentation for more details."); return TRUE; } static gboolean -handle_gateway6(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_gateway6(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { if (!is_ip6_address(scalar(node))) - return yaml_error(node, error, "invalid IPv6 address '%s'", scalar(node)); - set_str_if_null(cur_netdef->gateway6, scalar(node)); + return yaml_error(npp, node, error, "invalid IPv6 address '%s'", scalar(node)); + set_str_if_null(npp->current.netdef->gateway6, scalar(node)); g_warning("`gateway6` has been deprecated, use default routes instead.\n" "See the 'Default routes' section of the documentation for more details."); return TRUE; } static gboolean -handle_wifi_access_points(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_wifi_access_points(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) { + NetplanWifiAccessPoint *access_point = NULL; yaml_node_t* key, *value; gboolean ret = TRUE; - key = yaml_document_get_node(doc, entry->key); - assert_type(key, YAML_SCALAR_NODE); - value = yaml_document_get_node(doc, entry->value); - assert_type(value, YAML_MAPPING_NODE); + key = yaml_document_get_node(&npp->doc, entry->key); + assert_type(npp, key, YAML_SCALAR_NODE); + value = yaml_document_get_node(&npp->doc, entry->value); + assert_type(npp, value, YAML_MAPPING_NODE); - g_assert(cur_access_point == NULL); - cur_access_point = g_new0(NetplanWifiAccessPoint, 1); - cur_access_point->ssid = g_strdup(scalar(key)); - g_debug("%s: adding wifi AP '%s'", cur_netdef->id, cur_access_point->ssid); + g_assert(access_point == NULL); + access_point = g_new0(NetplanWifiAccessPoint, 1); + access_point->ssid = g_strdup(scalar(key)); + g_debug("%s: adding wifi AP '%s'", npp->current.netdef->id, access_point->ssid); /* Check if there's already an SSID with that name */ - if (cur_netdef->access_points && - g_hash_table_lookup(cur_netdef->access_points, cur_access_point->ssid)) { - ret = yaml_error(key, error, "%s: Duplicate access point SSID '%s'", cur_netdef->id, cur_access_point->ssid); + if (npp->current.netdef->access_points && + g_hash_table_lookup(npp->current.netdef->access_points, access_point->ssid)) { + ret = yaml_error(npp, key, error, "%s: Duplicate access point SSID '%s'", npp->current.netdef->id, access_point->ssid); } - if (!ret || !process_mapping(doc, value, wifi_access_point_handlers, NULL, error)) { - g_free(cur_access_point->ssid); - g_free(cur_access_point); /* XXX: should be more in-depth! */ - cur_access_point = NULL; + npp->current.access_point = access_point; + if (!ret || !process_mapping(npp, value, wifi_access_point_handlers, NULL, error)) { + access_point_clear(&npp->current.access_point, npp->current.backend); return FALSE; } - if (!cur_netdef->access_points) - cur_netdef->access_points = g_hash_table_new(g_str_hash, g_str_equal); - g_hash_table_insert(cur_netdef->access_points, cur_access_point->ssid, cur_access_point); - cur_access_point = NULL; + if (!npp->current.netdef->access_points) + npp->current.netdef->access_points = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(npp->current.netdef->access_points, access_point->ssid, access_point); + npp->current.access_point = NULL; } return TRUE; } /** - * Handler for bridge "interfaces:" list. We don't store that list in cur_netdef, - * but set cur_netdef's ID in all listed interfaces' "bond" or "bridge" field. + * Handler for bridge "interfaces:" list. We don't store that list in npp->current.netdef, + * but set npp->current.netdef's ID in all listed interfaces' "bond" or "bridge" field. * @data: ignored */ static gboolean -handle_bridge_interfaces(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_bridge_interfaces(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { /* all entries must refer to already defined IDs */ for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { - yaml_node_t *entry = yaml_document_get_node(doc, *i); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); NetplanNetDefinition *component; - assert_type(entry, YAML_SCALAR_NODE); - component = g_hash_table_lookup(netdefs, scalar(entry)); + assert_type(npp, entry, YAML_SCALAR_NODE); + component = g_hash_table_lookup(npp->parsed_defs, scalar(entry)); if (!component) { - add_missing_node(entry); + add_missing_node(npp, entry); } else { - if (component->bridge && g_strcmp0(component->bridge, cur_netdef->id) != 0) - return yaml_error(node, error, "%s: interface '%s' is already assigned to bridge %s", - cur_netdef->id, scalar(entry), component->bridge); + if (component->bridge && g_strcmp0(component->bridge, npp->current.netdef->id) != 0) + return yaml_error(npp, node, error, "%s: interface '%s' is already assigned to bridge %s", + npp->current.netdef->id, scalar(entry), component->bridge); if (component->bond) - return yaml_error(node, error, "%s: interface '%s' is already assigned to bond %s", - cur_netdef->id, scalar(entry), component->bond); - set_str_if_null(component->bridge, cur_netdef->id); + return yaml_error(npp, node, error, "%s: interface '%s' is already assigned to bond %s", + npp->current.netdef->id, scalar(entry), component->bond); + set_str_if_null(component->bridge, npp->current.netdef->id); if (component->backend == NETPLAN_BACKEND_OVS) { - g_debug("%s: Bridge contains openvswitch interface, choosing OVS backend", cur_netdef->id); - cur_netdef->backend = NETPLAN_BACKEND_OVS; + g_debug("%s: Bridge contains openvswitch interface, choosing OVS backend", npp->current.netdef->id); + npp->current.netdef->backend = NETPLAN_BACKEND_OVS; } } } @@ -1181,7 +1150,7 @@ handle_bridge_interfaces(yaml_document_t* doc, yaml_node_t* node, const void* da * located */ static gboolean -handle_bond_mode(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_bond_mode(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { if (!(strcmp(scalar(node), "balance-rr") == 0 || strcmp(scalar(node), "active-backup") == 0 || @@ -1192,17 +1161,17 @@ handle_bond_mode(yaml_document_t* doc, yaml_node_t* node, const void* data, GErr strcmp(scalar(node), "balance-alb") == 0 || strcmp(scalar(node), "balance-tcp") == 0 || // only supported for OVS strcmp(scalar(node), "balance-slb") == 0)) // only supported for OVS - return yaml_error(node, error, "unknown bond mode '%s'", scalar(node)); + return yaml_error(npp, node, error, "unknown bond mode '%s'", scalar(node)); /* Implicitly set NETPLAN_BACKEND_OVS if ovs-only mode selected */ if (!strcmp(scalar(node), "balance-tcp") || !strcmp(scalar(node), "balance-slb")) { g_debug("%s: mode '%s' only supported with openvswitch, choosing this backend", - cur_netdef->id, scalar(node)); - cur_netdef->backend = NETPLAN_BACKEND_OVS; + npp->current.netdef->id, scalar(node)); + npp->current.netdef->backend = NETPLAN_BACKEND_OVS; } - return handle_netdef_str(doc, node, data, error); + return handle_netdef_str(npp, node, data, error); } /** @@ -1210,28 +1179,28 @@ handle_bond_mode(yaml_document_t* doc, yaml_node_t* node, const void* data, GErr * @data: ignored */ static gboolean -handle_bond_interfaces(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_bond_interfaces(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { /* all entries must refer to already defined IDs */ for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { - yaml_node_t *entry = yaml_document_get_node(doc, *i); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); NetplanNetDefinition *component; - assert_type(entry, YAML_SCALAR_NODE); - component = g_hash_table_lookup(netdefs, scalar(entry)); + assert_type(npp, entry, YAML_SCALAR_NODE); + component = g_hash_table_lookup(npp->parsed_defs, scalar(entry)); if (!component) { - add_missing_node(entry); + add_missing_node(npp, entry); } else { if (component->bridge) - return yaml_error(node, error, "%s: interface '%s' is already assigned to bridge %s", - cur_netdef->id, scalar(entry), component->bridge); - if (component->bond && g_strcmp0(component->bond, cur_netdef->id) != 0) - return yaml_error(node, error, "%s: interface '%s' is already assigned to bond %s", - cur_netdef->id, scalar(entry), component->bond); - component->bond = g_strdup(cur_netdef->id); + return yaml_error(npp, node, error, "%s: interface '%s' is already assigned to bridge %s", + npp->current.netdef->id, scalar(entry), component->bridge); + if (component->bond && g_strcmp0(component->bond, npp->current.netdef->id) != 0) + return yaml_error(npp, node, error, "%s: interface '%s' is already assigned to bond %s", + npp->current.netdef->id, scalar(entry), component->bond); + component->bond = g_strdup(npp->current.netdef->id); if (component->backend == NETPLAN_BACKEND_OVS) { - g_debug("%s: Bond contains openvswitch interface, choosing OVS backend", cur_netdef->id); - cur_netdef->backend = NETPLAN_BACKEND_OVS; + g_debug("%s: Bond contains openvswitch interface, choosing OVS backend", npp->current.netdef->id); + npp->current.netdef->backend = NETPLAN_BACKEND_OVS; } } } @@ -1241,71 +1210,71 @@ handle_bond_interfaces(yaml_document_t* doc, yaml_node_t* node, const void* data static gboolean -handle_nameservers_search(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_nameservers_search(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { - yaml_node_t *entry = yaml_document_get_node(doc, *i); - assert_type(entry, YAML_SCALAR_NODE); - if (!cur_netdef->search_domains) - cur_netdef->search_domains = g_array_new(FALSE, FALSE, sizeof(char*)); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); + assert_type(npp, entry, YAML_SCALAR_NODE); + if (!npp->current.netdef->search_domains) + npp->current.netdef->search_domains = g_array_new(FALSE, FALSE, sizeof(char*)); char* s = g_strdup(scalar(entry)); - g_array_append_val(cur_netdef->search_domains, s); + g_array_append_val(npp->current.netdef->search_domains, s); } return TRUE; } static gboolean -handle_nameservers_addresses(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_nameservers_addresses(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { - yaml_node_t *entry = yaml_document_get_node(doc, *i); - assert_type(entry, YAML_SCALAR_NODE); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); + assert_type(npp, entry, YAML_SCALAR_NODE); /* is it an IPv4 address? */ if (is_ip4_address(scalar(entry))) { - if (!cur_netdef->ip4_nameservers) - cur_netdef->ip4_nameservers = g_array_new(FALSE, FALSE, sizeof(char*)); + if (!npp->current.netdef->ip4_nameservers) + npp->current.netdef->ip4_nameservers = g_array_new(FALSE, FALSE, sizeof(char*)); char* s = g_strdup(scalar(entry)); - g_array_append_val(cur_netdef->ip4_nameservers, s); + g_array_append_val(npp->current.netdef->ip4_nameservers, s); continue; } /* is it an IPv6 address? */ if (is_ip6_address(scalar(entry))) { - if (!cur_netdef->ip6_nameservers) - cur_netdef->ip6_nameservers = g_array_new(FALSE, FALSE, sizeof(char*)); + if (!npp->current.netdef->ip6_nameservers) + npp->current.netdef->ip6_nameservers = g_array_new(FALSE, FALSE, sizeof(char*)); char* s = g_strdup(scalar(entry)); - g_array_append_val(cur_netdef->ip6_nameservers, s); + g_array_append_val(npp->current.netdef->ip6_nameservers, s); continue; } - return yaml_error(node, error, "malformed address '%s', must be X.X.X.X or X:X:X:X:X:X:X:X", scalar(entry)); + return yaml_error(npp, node, error, "malformed address '%s', must be X.X.X.X or X:X:X:X:X:X:X:X", scalar(entry)); } return TRUE; } static gboolean -handle_link_local(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_link_local(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { gboolean ipv4 = FALSE; gboolean ipv6 = FALSE; for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { - yaml_node_t *entry = yaml_document_get_node(doc, *i); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); - assert_type(entry, YAML_SCALAR_NODE); + assert_type(npp, entry, YAML_SCALAR_NODE); if (g_ascii_strcasecmp(scalar(entry), "ipv4") == 0) ipv4 = TRUE; else if (g_ascii_strcasecmp(scalar(entry), "ipv6") == 0) ipv6 = TRUE; else - return yaml_error(node, error, "invalid value for link-local: '%s'", scalar(entry)); + return yaml_error(npp, node, error, "invalid value for link-local: '%s'", scalar(entry)); } - cur_netdef->linklocal.ipv4 = ipv4; - cur_netdef->linklocal.ipv6 = ipv6; + npp->current.netdef->linklocal.ipv4 = ipv4; + npp->current.netdef->linklocal.ipv6 = ipv6; return TRUE; } @@ -1321,22 +1290,22 @@ NETPLAN_OPTIONAL_ADDRESS_TYPES[] = { }; static gboolean -handle_optional_addresses(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_optional_addresses(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { - yaml_node_t *entry = yaml_document_get_node(doc, *i); - assert_type(entry, YAML_SCALAR_NODE); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); + assert_type(npp, entry, YAML_SCALAR_NODE); int found = FALSE; for (unsigned i = 0; NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name != NULL; ++i) { if (g_ascii_strcasecmp(scalar(entry), NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name) == 0) { - cur_netdef->optional_addresses |= NETPLAN_OPTIONAL_ADDRESS_TYPES[i].flag; + npp->current.netdef->optional_addresses |= NETPLAN_OPTIONAL_ADDRESS_TYPES[i].flag; found = TRUE; break; } } if (!found) { - return yaml_error(node, error, "invalid value for optional-addresses: '%s'", scalar(entry)); + return yaml_error(npp, node, error, "invalid value for optional-addresses: '%s'", scalar(entry)); } } return TRUE; @@ -1376,55 +1345,58 @@ check_and_set_family(int family, guint* dest) /* TODO: (cyphermox) Refactor the functions below. There's a lot of room for reuse. */ static gboolean -handle_routes_bool(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_routes_bool(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - g_assert(cur_route); - return handle_generic_bool(doc, node, cur_route, data, error); + g_assert(npp->current.route); + return handle_generic_bool(npp, node, npp->current.route, data, error); } static gboolean -handle_routes_scope(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_routes_scope(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - if (cur_route->scope) - g_free(cur_route->scope); - cur_route->scope = g_strdup(scalar(node)); + NetplanIPRoute* route = npp->current.route; + if (route->scope) + g_free(route->scope); + route->scope = g_strdup(scalar(node)); - if (g_ascii_strcasecmp(cur_route->scope, "global") == 0 || - g_ascii_strcasecmp(cur_route->scope, "link") == 0 || - g_ascii_strcasecmp(cur_route->scope, "host") == 0) + if (g_ascii_strcasecmp(route->scope, "global") == 0 || + g_ascii_strcasecmp(route->scope, "link") == 0 || + g_ascii_strcasecmp(route->scope, "host") == 0) return TRUE; - return yaml_error(node, error, "invalid route scope '%s'", cur_route->scope); + return yaml_error(npp, node, error, "invalid route scope '%s'", route->scope); } static gboolean -handle_routes_type(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_routes_type(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - if (cur_route->type) - g_free(cur_route->type); - cur_route->type = g_strdup(scalar(node)); + NetplanIPRoute* route = npp->current.route; + if (route->type) + g_free(route->type); + route->type = g_strdup(scalar(node)); - if (g_ascii_strcasecmp(cur_route->type, "unicast") == 0 || - g_ascii_strcasecmp(cur_route->type, "unreachable") == 0 || - g_ascii_strcasecmp(cur_route->type, "blackhole") == 0 || - g_ascii_strcasecmp(cur_route->type, "prohibit") == 0) + if (g_ascii_strcasecmp(route->type, "unicast") == 0 || + g_ascii_strcasecmp(route->type, "unreachable") == 0 || + g_ascii_strcasecmp(route->type, "blackhole") == 0 || + g_ascii_strcasecmp(route->type, "prohibit") == 0) return TRUE; - return yaml_error(node, error, "invalid route type '%s'", cur_route->type); + return yaml_error(npp, node, error, "invalid route type '%s'", route->type); } static gboolean -handle_routes_ip(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_routes_ip(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { + NetplanIPRoute* route = npp->current.route; guint offset = GPOINTER_TO_UINT(data); int family = get_ip_family(scalar(node)); - char** dest = (char**) ((void*) cur_route + offset); + char** dest = (char**) ((void*) route + offset); if (family < 0) - return yaml_error(node, error, "invalid IP family '%d'", family); + return yaml_error(npp, node, error, "invalid IP family '%d'", family); - if (!check_and_set_family(family, &cur_route->family)) - return yaml_error(node, error, "IP family mismatch in route to %s", scalar(node)); + if (!check_and_set_family(family, &route->family)) + return yaml_error(npp, node, error, "IP family mismatch in route to %s", scalar(node)); g_free(*dest); *dest = g_strdup(scalar(node)); @@ -1433,27 +1405,28 @@ handle_routes_ip(yaml_document_t* doc, yaml_node_t* node, const void* data, GErr } static gboolean -handle_routes_destination(yaml_document_t *doc, yaml_node_t *node, const void *data, GError **error) +handle_routes_destination(NetplanParser *npp, yaml_node_t *node, const void *data, GError **error) { const char *addr = scalar(node); if (g_strcmp0(addr, "default") != 0) /* netplan-feature: default-routes */ - return handle_routes_ip(doc, node, route_offset(to), error); - set_str_if_null(cur_route->to, addr); + return handle_routes_ip(npp, node, route_offset(to), error); + set_str_if_null(npp->current.route->to, addr); return TRUE; } static gboolean -handle_ip_rule_ip(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_ip_rule_ip(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { + NetplanIPRule* ip_rule = npp->current.ip_rule; guint offset = GPOINTER_TO_UINT(data); int family = get_ip_family(scalar(node)); - char** dest = (char**) ((void*) cur_ip_rule + offset); + char** dest = (char**) ((void*) ip_rule + offset); if (family < 0) - return yaml_error(node, error, "invalid IP family '%d'", family); + return yaml_error(npp, node, error, "invalid IP family '%d'", family); - if (!check_and_set_family(family, &cur_ip_rule->family)) - return yaml_error(node, error, "IP family mismatch in route to %s", scalar(node)); + if (!check_and_set_family(family, &ip_rule->family)) + return yaml_error(npp, node, error, "IP family mismatch in route to %s", scalar(node)); g_free(*dest); *dest = g_strdup(scalar(node)); @@ -1462,25 +1435,26 @@ handle_ip_rule_ip(yaml_document_t* doc, yaml_node_t* node, const void* data, GEr } static gboolean -handle_ip_rule_guint(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_ip_rule_guint(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - g_assert(cur_ip_rule); - return handle_generic_guint(doc, node, cur_ip_rule, data, error); + g_assert(npp->current.ip_rule); + return handle_generic_guint(npp, node, npp->current.ip_rule, data, error); } static gboolean -handle_routes_guint(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_routes_guint(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - g_assert(cur_route); - return handle_generic_guint(doc, node, cur_route, data, error); + g_assert(npp->current.route); + return handle_generic_guint(npp, node, npp->current.route, data, error); } static gboolean -handle_ip_rule_tos(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_ip_rule_tos(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - gboolean ret = handle_generic_guint(doc, node, cur_ip_rule, data, error); - if (cur_ip_rule->tos > 255) - return yaml_error(node, error, "invalid ToS (must be between 0 and 255): %s", scalar(node)); + NetplanIPRule* ip_rule = npp->current.ip_rule; + gboolean ret = handle_generic_guint(npp, node, ip_rule, data, error); + if (ip_rule->tos > 255) + return yaml_error(npp, node, error, "invalid ToS (must be between 0 and 255): %s", scalar(node)); return ret; } @@ -1489,7 +1463,7 @@ handle_ip_rule_tos(yaml_document_t* doc, yaml_node_t* node, const void* data, GE ****************************************************/ static gboolean -handle_bridge_path_cost(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_bridge_path_cost(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) { yaml_node_t* key, *value; @@ -1498,25 +1472,25 @@ handle_bridge_path_cost(yaml_document_t* doc, yaml_node_t* node, const void* dat NetplanNetDefinition *component; guint* ref_ptr; - key = yaml_document_get_node(doc, entry->key); - assert_type(key, YAML_SCALAR_NODE); - value = yaml_document_get_node(doc, entry->value); - assert_type(value, YAML_SCALAR_NODE); + key = yaml_document_get_node(&npp->doc, entry->key); + assert_type(npp, key, YAML_SCALAR_NODE); + value = yaml_document_get_node(&npp->doc, entry->value); + assert_type(npp, value, YAML_SCALAR_NODE); - component = g_hash_table_lookup(netdefs, scalar(key)); + component = g_hash_table_lookup(npp->parsed_defs, scalar(key)); if (!component) { - add_missing_node(key); + add_missing_node(npp, key); } else { ref_ptr = ((guint*) ((void*) component + GPOINTER_TO_UINT(data))); if (*ref_ptr) - return yaml_error(node, error, "%s: interface '%s' already has a path cost of %u", - cur_netdef->id, scalar(key), *ref_ptr); + return yaml_error(npp, node, error, "%s: interface '%s' already has a path cost of %u", + npp->current.netdef->id, scalar(key), *ref_ptr); v = g_ascii_strtoull(scalar(value), &endptr, 10); if (*endptr != '\0' || v > G_MAXUINT) - return yaml_error(node, error, "invalid unsigned int value '%s'", scalar(value)); + return yaml_error(npp, node, error, "invalid unsigned int value '%s'", scalar(value)); - g_debug("%s: adding path '%s' of cost: %d", cur_netdef->id, scalar(key), v); + g_debug("%s: adding path '%s' of cost: %d", npp->current.netdef->id, scalar(key), v); *ref_ptr = v; } @@ -1525,7 +1499,7 @@ handle_bridge_path_cost(yaml_document_t* doc, yaml_node_t* node, const void* dat } static gboolean -handle_bridge_port_priority(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_bridge_port_priority(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) { yaml_node_t* key, *value; @@ -1534,26 +1508,26 @@ handle_bridge_port_priority(yaml_document_t* doc, yaml_node_t* node, const void* NetplanNetDefinition *component; guint* ref_ptr; - key = yaml_document_get_node(doc, entry->key); - assert_type(key, YAML_SCALAR_NODE); - value = yaml_document_get_node(doc, entry->value); - assert_type(value, YAML_SCALAR_NODE); + key = yaml_document_get_node(&npp->doc, entry->key); + assert_type(npp, key, YAML_SCALAR_NODE); + value = yaml_document_get_node(&npp->doc, entry->value); + assert_type(npp, value, YAML_SCALAR_NODE); - component = g_hash_table_lookup(netdefs, scalar(key)); + component = g_hash_table_lookup(npp->parsed_defs, scalar(key)); if (!component) { - add_missing_node(key); + add_missing_node(npp, key); } else { ref_ptr = ((guint*) ((void*) component + GPOINTER_TO_UINT(data))); if (*ref_ptr) - return yaml_error(node, error, "%s: interface '%s' already has a port priority of %u", - cur_netdef->id, scalar(key), *ref_ptr); + return yaml_error(npp, node, error, "%s: interface '%s' already has a port priority of %u", + npp->current.netdef->id, scalar(key), *ref_ptr); v = g_ascii_strtoull(scalar(value), &endptr, 10); if (*endptr != '\0' || v > 63) - return yaml_error(node, error, "invalid port priority value (must be between 0 and 63): %s", + return yaml_error(npp, node, error, "invalid port priority value (must be between 0 and 63): %s", scalar(value)); - g_debug("%s: adding port '%s' of priority: %d", cur_netdef->id, scalar(key), v); + g_debug("%s: adding port '%s' of priority: %d", npp->current.netdef->id, scalar(key), v); *ref_ptr = v; } @@ -1574,11 +1548,11 @@ static const mapping_entry_handler bridge_params_handlers[] = { }; static gboolean -handle_bridge(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_bridge(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - cur_netdef->custom_bridging = TRUE; - cur_netdef->bridge_params.stp = TRUE; - return process_mapping(doc, node, bridge_params_handlers, NULL, error); + npp->current.netdef->custom_bridging = TRUE; + npp->current.netdef->bridge_params.stp = TRUE; + return process_mapping(npp, node, bridge_params_handlers, NULL, error); } /**************************************************** @@ -1601,59 +1575,63 @@ static const mapping_entry_handler routes_handlers[] = { }; static gboolean -handle_routes(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_routes(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - if (!cur_netdef->routes) - cur_netdef->routes = g_array_new(FALSE, TRUE, sizeof(NetplanIPRoute*)); + if (!npp->current.netdef->routes) + npp->current.netdef->routes = g_array_new(FALSE, TRUE, sizeof(NetplanIPRoute*)); /* Avoid adding the same routes in a 2nd parsing pass by comparing * the array size to the YAML sequence size. Skip if they are equal. */ guint item_count = node->data.sequence.items.top - node->data.sequence.items.start; - if (cur_netdef->routes->len == item_count) { - g_debug("%s: all routes have already been added", cur_netdef->id); + if (npp->current.netdef->routes->len == item_count) { + g_debug("%s: all routes have already been added", npp->current.netdef->id); return TRUE; } for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { - yaml_node_t *entry = yaml_document_get_node(doc, *i); - assert_type(entry, YAML_MAPPING_NODE); - - g_assert(cur_route == NULL); - cur_route = g_new0(NetplanIPRoute, 1); - cur_route->type = g_strdup("unicast"); - cur_route->scope = g_strdup("global"); - cur_route->family = G_MAXUINT; /* 0 is a valid family ID */ - cur_route->metric = NETPLAN_METRIC_UNSPEC; /* 0 is a valid metric */ - cur_route->table = NETPLAN_ROUTE_TABLE_UNSPEC; - g_debug("%s: adding new route", cur_netdef->id); - - if (!process_mapping(doc, entry, routes_handlers, NULL, error)) + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); + NetplanIPRoute* route; + + assert_type(npp, entry, YAML_MAPPING_NODE); + + g_assert(npp->current.route == NULL); + route = g_new0(NetplanIPRoute, 1); + route->type = g_strdup("unicast"); + route->scope = g_strdup("global"); + route->family = G_MAXUINT; /* 0 is a valid family ID */ + route->metric = NETPLAN_METRIC_UNSPEC; /* 0 is a valid metric */ + route->table = NETPLAN_ROUTE_TABLE_UNSPEC; + g_debug("%s: adding new route", npp->current.netdef->id); + + npp->current.route = route; + + if (!process_mapping(npp, entry, routes_handlers, NULL, error)) goto err; - if ( ( g_ascii_strcasecmp(cur_route->scope, "link") == 0 - || g_ascii_strcasecmp(cur_route->scope, "host") == 0) - && !cur_route->to) { - yaml_error(node, error, "link and host routes must specify a 'to' IP"); + if ( ( g_ascii_strcasecmp(route->scope, "link") == 0 + || g_ascii_strcasecmp(route->scope, "host") == 0) + && !route->to) { + yaml_error(npp, node, error, "link and host routes must specify a 'to' IP"); goto err; - } else if ( g_ascii_strcasecmp(cur_route->type, "unicast") == 0 - && g_ascii_strcasecmp(cur_route->scope, "global") == 0 - && (!cur_route->to || !cur_route->via)) { - yaml_error(node, error, "global unicast route must include both a 'to' and 'via' IP"); + } else if ( g_ascii_strcasecmp(route->type, "unicast") == 0 + && g_ascii_strcasecmp(route->scope, "global") == 0 + && (!route->to || !route->via)) { + yaml_error(npp, node, error, "global unicast route must include both a 'to' and 'via' IP"); goto err; - } else if (g_ascii_strcasecmp(cur_route->type, "unicast") != 0 && !cur_route->to) { - yaml_error(node, error, "non-unicast routes must specify a 'to' IP"); + } else if (g_ascii_strcasecmp(route->type, "unicast") != 0 && !route->to) { + yaml_error(npp, node, error, "non-unicast routes must specify a 'to' IP"); goto err; } - g_array_append_val(cur_netdef->routes, cur_route); - cur_route = NULL; + g_array_append_val(npp->current.netdef->routes, route); + npp->current.route = NULL; } return TRUE; err: - if (cur_route) { - g_free(cur_route); - cur_route = NULL; + if (npp->current.route) { + g_free(npp->current.route); + npp->current.route = NULL; } return FALSE; } @@ -1669,33 +1647,34 @@ static const mapping_entry_handler ip_rules_handlers[] = { }; static gboolean -handle_ip_rules(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_ip_rules(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { - yaml_node_t *entry = yaml_document_get_node(doc, *i); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); gboolean ret; - cur_ip_rule = g_new0(NetplanIPRule, 1); - cur_ip_rule->family = G_MAXUINT; /* 0 is a valid family ID */ - cur_ip_rule->priority = NETPLAN_IP_RULE_PRIO_UNSPEC; - cur_ip_rule->table = NETPLAN_ROUTE_TABLE_UNSPEC; - cur_ip_rule->tos = NETPLAN_IP_RULE_TOS_UNSPEC; - cur_ip_rule->fwmark = NETPLAN_IP_RULE_FW_MARK_UNSPEC; + NetplanIPRule* ip_rule = g_new0(NetplanIPRule, 1); + ip_rule->family = G_MAXUINT; /* 0 is a valid family ID */ + ip_rule->priority = NETPLAN_IP_RULE_PRIO_UNSPEC; + ip_rule->table = NETPLAN_ROUTE_TABLE_UNSPEC; + ip_rule->tos = NETPLAN_IP_RULE_TOS_UNSPEC; + ip_rule->fwmark = NETPLAN_IP_RULE_FW_MARK_UNSPEC; + + npp->current.ip_rule = ip_rule; + ret = process_mapping(npp, entry, ip_rules_handlers, NULL, error); + npp->current.ip_rule = NULL; - ret = process_mapping(doc, entry, ip_rules_handlers, NULL, error); - if (ret && !cur_ip_rule->from && !cur_ip_rule->to) - ret = yaml_error(node, error, "IP routing policy must include either a 'from' or 'to' IP"); + if (ret && !ip_rule->from && !ip_rule->to) + ret = yaml_error(npp, node, error, "IP routing policy must include either a 'from' or 'to' IP"); if (!ret) { - g_free(cur_ip_rule); /* XXX: do an in-depth cleaning */ - cur_ip_rule = NULL; + ip_rule_clear(&ip_rule); return FALSE; } - if (!cur_netdef->ip_rules) - cur_netdef->ip_rules = g_array_new(FALSE, FALSE, sizeof(NetplanIPRule*)); - g_array_append_val(cur_netdef->ip_rules, cur_ip_rule); - cur_ip_rule = NULL; + if (!npp->current.netdef->ip_rules) + npp->current.netdef->ip_rules = g_array_new(FALSE, FALSE, sizeof(NetplanIPRule*)); + g_array_append_val(npp->current.netdef->ip_rules, ip_rule); } return TRUE; } @@ -1705,60 +1684,60 @@ handle_ip_rules(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** ****************************************************/ static gboolean -handle_arp_ip_targets(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_arp_ip_targets(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - if (!cur_netdef->bond_params.arp_ip_targets) { - cur_netdef->bond_params.arp_ip_targets = g_array_new(FALSE, FALSE, sizeof(char *)); + if (!npp->current.netdef->bond_params.arp_ip_targets) { + npp->current.netdef->bond_params.arp_ip_targets = g_array_new(FALSE, FALSE, sizeof(char *)); } /* Avoid adding the same arp_ip_targets in a 2nd parsing pass by comparing * the array size to the YAML sequence size. Skip if they are equal. */ guint item_count = node->data.sequence.items.top - node->data.sequence.items.start; - if (cur_netdef->bond_params.arp_ip_targets->len == item_count) { - g_debug("%s: all arp ip targets have already been added", cur_netdef->id); + if (npp->current.netdef->bond_params.arp_ip_targets->len == item_count) { + g_debug("%s: all arp ip targets have already been added", npp->current.netdef->id); return TRUE; } for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { g_autofree char* addr = NULL; - yaml_node_t *entry = yaml_document_get_node(doc, *i); - assert_type(entry, YAML_SCALAR_NODE); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); + assert_type(npp, entry, YAML_SCALAR_NODE); addr = g_strdup(scalar(entry)); /* is it an IPv4 address? */ if (is_ip4_address(addr)) { char* s = g_strdup(scalar(entry)); - g_array_append_val(cur_netdef->bond_params.arp_ip_targets, s); + g_array_append_val(npp->current.netdef->bond_params.arp_ip_targets, s); continue; } - return yaml_error(node, error, "malformed address '%s', must be X.X.X.X or X:X:X:X:X:X:X:X", scalar(entry)); + return yaml_error(npp, node, error, "malformed address '%s', must be X.X.X.X or X:X:X:X:X:X:X:X", scalar(entry)); } return TRUE; } static gboolean -handle_bond_primary_slave(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_bond_primary_slave(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { NetplanNetDefinition *component; char** ref_ptr; - component = g_hash_table_lookup(netdefs, scalar(node)); + component = g_hash_table_lookup(npp->parsed_defs, scalar(node)); if (!component) { - add_missing_node(node); + add_missing_node(npp, node); } else { /* If this is not the primary pass, the primary slave might already be equally set. */ - if (!g_strcmp0(cur_netdef->bond_params.primary_slave, scalar(node))) { + if (!g_strcmp0(npp->current.netdef->bond_params.primary_slave, scalar(node))) { return TRUE; - } else if (cur_netdef->bond_params.primary_slave) - return yaml_error(node, error, "%s: bond already has a primary slave: %s", - cur_netdef->id, cur_netdef->bond_params.primary_slave); + } else if (npp->current.netdef->bond_params.primary_slave) + return yaml_error(npp, node, error, "%s: bond already has a primary slave: %s", + npp->current.netdef->id, npp->current.netdef->bond_params.primary_slave); ref_ptr = ((char**) ((void*) component + GPOINTER_TO_UINT(data))); *ref_ptr = g_strdup(scalar(node)); - cur_netdef->bond_params.primary_slave = g_strdup(scalar(node)); + npp->current.netdef->bond_params.primary_slave = g_strdup(scalar(node)); } return TRUE; @@ -1793,26 +1772,26 @@ static const mapping_entry_handler bond_params_handlers[] = { }; static gboolean -handle_bonding(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_bonding(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - return process_mapping(doc, node, bond_params_handlers, NULL, error); + return process_mapping(npp, node, bond_params_handlers, NULL, error); } static gboolean -handle_dhcp_identifier(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_dhcp_identifier(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - g_free(cur_netdef->dhcp_identifier); + g_free(npp->current.netdef->dhcp_identifier); /* "duid" is the default case, so we don't store it. */ if (g_ascii_strcasecmp(scalar(node), "duid") != 0) - cur_netdef->dhcp_identifier = g_strdup(scalar(node)); + npp->current.netdef->dhcp_identifier = g_strdup(scalar(node)); else - cur_netdef->dhcp_identifier = NULL; + npp->current.netdef->dhcp_identifier = NULL; - if (cur_netdef->dhcp_identifier == NULL || - g_ascii_strcasecmp(cur_netdef->dhcp_identifier, "mac") == 0) + if (npp->current.netdef->dhcp_identifier == NULL || + g_ascii_strcasecmp(npp->current.netdef->dhcp_identifier, "mac") == 0) return TRUE; - return yaml_error(node, error, "invalid DHCP client identifier type '%s'", cur_netdef->dhcp_identifier); + return yaml_error(npp, node, error, "invalid DHCP client identifier type '%s'", npp->current.netdef->dhcp_identifier); } /**************************************************** @@ -1820,7 +1799,7 @@ handle_dhcp_identifier(yaml_document_t* doc, yaml_node_t* node, const void* data ****************************************************/ static gboolean -handle_tunnel_addr(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_tunnel_addr(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { g_autofree char* addr = NULL; char* prefix_len; @@ -1829,21 +1808,21 @@ handle_tunnel_addr(yaml_document_t* doc, yaml_node_t* node, const void* data, GE addr = g_strdup(scalar(node)); prefix_len = strrchr(addr, '/'); if (prefix_len) - return yaml_error(node, error, "address '%s' should not include /prefixlength", scalar(node)); + return yaml_error(npp, node, error, "address '%s' should not include /prefixlength", scalar(node)); /* is it an IPv4 address? */ if (is_ip4_address(addr)) - return handle_netdef_ip4(doc, node, data, error); + return handle_netdef_ip4(npp, node, data, error); /* is it an IPv6 address? */ if (is_ip6_address(addr)) - return handle_netdef_ip6(doc, node, data, error); + return handle_netdef_ip6(npp, node, data, error); - return yaml_error(node, error, "malformed address '%s', must be X.X.X.X or X:X:X:X:X:X:X:X", scalar(node)); + return yaml_error(npp, node, error, "malformed address '%s', must be X.X.X.X or X:X:X:X:X:X:X:X", scalar(node)); } static gboolean -handle_tunnel_mode(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_tunnel_mode(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { const char *key = scalar(node); NetplanTunnelMode i; @@ -1851,12 +1830,12 @@ handle_tunnel_mode(yaml_document_t* doc, yaml_node_t* node, const void* _, GErro // Skip over unknown (0) tunnel mode. for (i = 1; i < NETPLAN_TUNNEL_MODE_MAX_; ++i) { if (g_strcmp0(netplan_tunnel_mode_name(i), key) == 0) { - cur_netdef->tunnel.mode = i; + npp->current.netdef->tunnel.mode = i; return TRUE; } } - return yaml_error(node, error, "%s: tunnel mode '%s' is not supported", cur_netdef->id, key); + return yaml_error(npp, node, error, "%s: tunnel mode '%s' is not supported", npp->current.netdef->id, key); } static const mapping_entry_handler tunnel_keys_handlers[] = { @@ -1867,7 +1846,7 @@ static const mapping_entry_handler tunnel_keys_handlers[] = { }; static gboolean -handle_tunnel_key_mapping(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_tunnel_key_mapping(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { gboolean ret = FALSE; @@ -1875,15 +1854,15 @@ handle_tunnel_key_mapping(yaml_document_t* doc, yaml_node_t* node, const void* _ * single scalar with the same key to use for both input, output and private * keys, or a mapping where one can specify each. */ if (node->type == YAML_SCALAR_NODE) { - ret = handle_netdef_str(doc, node, netdef_offset(tunnel.input_key), error); + ret = handle_netdef_str(npp, node, netdef_offset(tunnel.input_key), error); if (ret) - ret = handle_netdef_str(doc, node, netdef_offset(tunnel.output_key), error); + ret = handle_netdef_str(npp, node, netdef_offset(tunnel.output_key), error); if (ret) - ret = handle_netdef_str(doc, node, netdef_offset(tunnel.private_key), error); + ret = handle_netdef_str(npp, node, netdef_offset(tunnel.private_key), error); } else if (node->type == YAML_MAPPING_NODE) - ret = process_mapping(doc, node, tunnel_keys_handlers, NULL, error); + ret = process_mapping(npp, node, tunnel_keys_handlers, NULL, error); else - return yaml_error(node, error, "invalid type for 'key[s]': must be a scalar or mapping"); + return yaml_error(npp, node, error, "invalid type for 'key[s]': must be a scalar or mapping"); return ret; } @@ -1893,10 +1872,10 @@ handle_tunnel_key_mapping(yaml_document_t* doc, yaml_node_t* node, const void* _ * @data: pointer to the const char* field to write */ static gboolean -handle_wireguard_peer_str(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_wireguard_peer_str(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - g_assert(cur_wireguard_peer); - return handle_generic_str(doc, node, cur_wireguard_peer, data, error); + g_assert(npp->current.wireguard_peer); + return handle_generic_str(npp, node, npp->current.wireguard_peer, data, error); } /** @@ -1904,21 +1883,21 @@ handle_wireguard_peer_str(yaml_document_t* doc, yaml_node_t* node, const void* d * @data: pointer to the guint field to write */ static gboolean -handle_wireguard_peer_guint(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_wireguard_peer_guint(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - g_assert(cur_wireguard_peer); - return handle_generic_guint(doc, node, cur_wireguard_peer, data, error); + g_assert(npp->current.wireguard_peer); + return handle_generic_guint(npp, node, npp->current.wireguard_peer, data, error); } static gboolean -handle_wireguard_allowed_ips(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_wireguard_allowed_ips(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - return handle_generic_addresses(doc, node, FALSE, &(cur_wireguard_peer->allowed_ips), - &(cur_wireguard_peer->allowed_ips), error); + return handle_generic_addresses(npp, node, FALSE, &(npp->current.wireguard_peer->allowed_ips), + &(npp->current.wireguard_peer->allowed_ips), error); } static gboolean -handle_wireguard_endpoint(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_wireguard_endpoint(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { g_autofree char* endpoint = NULL; char* port; @@ -1928,13 +1907,13 @@ handle_wireguard_endpoint(yaml_document_t* doc, yaml_node_t* node, const void* _ endpoint = g_strdup(scalar(node)); /* absolute minimal length of endpoint is 3 chars: 'h:8' */ if (strlen(endpoint) < 3) { - return yaml_error(node, error, "invalid endpoint address or hostname '%s'", scalar(node)); + return yaml_error(npp, node, error, "invalid endpoint address or hostname '%s'", scalar(node)); } if (endpoint[0] == '[') { /* this is an ipv6 endpoint in [ad:rr:ee::ss]:port form */ char *endbrace = strrchr(endpoint, ']'); if (!endbrace) - return yaml_error(node, error, "invalid address in endpoint '%s'", scalar(node)); + return yaml_error(npp, node, error, "invalid address in endpoint '%s'", scalar(node)); address = endpoint + 1; *endbrace = '\0'; port = strrchr(endbrace + 1, ':'); @@ -1944,16 +1923,16 @@ handle_wireguard_endpoint(yaml_document_t* doc, yaml_node_t* node, const void* _ } /* split off :port */ if (!port) - return yaml_error(node, error, "endpoint '%s' is missing :port", scalar(node)); + return yaml_error(npp, node, error, "endpoint '%s' is missing :port", scalar(node)); *port = '\0'; port++; /* skip former ':' into first char of port */ port_num = g_ascii_strtoull(port, NULL, 10); if (port_num > 65535) - return yaml_error(node, error, "invalid port in endpoint '%s'", scalar(node)); + return yaml_error(npp, node, error, "invalid port in endpoint '%s'", scalar(node)); if (is_ip4_address(address) || is_ip6_address(address) || is_hostname(address)) { - return handle_wireguard_peer_str(doc, node, wireguard_peer_offset(endpoint), error); + return handle_wireguard_peer_str(npp, node, wireguard_peer_offset(endpoint), error); } - return yaml_error(node, error, "invalid endpoint address or hostname '%s'", scalar(node)); + return yaml_error(npp, node, error, "invalid endpoint address or hostname '%s'", scalar(node)); } static const mapping_entry_handler wireguard_peer_keys_handlers[] = { @@ -1963,9 +1942,9 @@ static const mapping_entry_handler wireguard_peer_keys_handlers[] = { }; static gboolean -handle_wireguard_peer_key_mapping(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_wireguard_peer_key_mapping(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - return process_mapping(doc, node, wireguard_peer_keys_handlers, NULL, error); + return process_mapping(npp, node, wireguard_peer_keys_handlers, NULL, error); } const mapping_entry_handler wireguard_peer_handlers[] = { @@ -1977,36 +1956,34 @@ const mapping_entry_handler wireguard_peer_handlers[] = { }; static gboolean -handle_wireguard_peers(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_wireguard_peers(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - if (!cur_netdef->wireguard_peers) - cur_netdef->wireguard_peers = g_array_new(FALSE, TRUE, sizeof(NetplanWireguardPeer*)); + if (!npp->current.netdef->wireguard_peers) + npp->current.netdef->wireguard_peers = g_array_new(FALSE, TRUE, sizeof(NetplanWireguardPeer*)); /* Avoid adding the same peers in a 2nd parsing pass by comparing * the array size to the YAML sequence size. Skip if they are equal. */ guint item_count = node->data.sequence.items.top - node->data.sequence.items.start; - if (cur_netdef->wireguard_peers->len == item_count) { - g_debug("%s: all wireguard peers have already been added", cur_netdef->id); + if (npp->current.netdef->wireguard_peers->len == item_count) { + g_debug("%s: all wireguard peers have already been added", npp->current.netdef->id); return TRUE; } for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { - yaml_node_t *entry = yaml_document_get_node(doc, *i); - assert_type(entry, YAML_MAPPING_NODE); - - g_assert(cur_wireguard_peer == NULL); - cur_wireguard_peer = g_new0(NetplanWireguardPeer, 1); - cur_wireguard_peer->allowed_ips = g_array_new(FALSE, FALSE, sizeof(char*)); - g_debug("%s: adding new wireguard peer", cur_netdef->id); - - if (!process_mapping(doc, entry, wireguard_peer_handlers, NULL, error)) { - g_array_free(cur_wireguard_peer->allowed_ips, TRUE); - g_free(cur_wireguard_peer); /* TODO: in-depth cleaning ! */ - cur_wireguard_peer = NULL; + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); + assert_type(npp, entry, YAML_MAPPING_NODE); + + g_assert(npp->current.wireguard_peer == NULL); + npp->current.wireguard_peer = g_new0(NetplanWireguardPeer, 1); + npp->current.wireguard_peer->allowed_ips = g_array_new(FALSE, FALSE, sizeof(char*)); + g_debug("%s: adding new wireguard peer", npp->current.netdef->id); + + if (!process_mapping(npp, entry, wireguard_peer_handlers, NULL, error)) { + wireguard_peer_clear(&npp->current.wireguard_peer); return FALSE; } - g_array_append_val(cur_netdef->wireguard_peers, cur_wireguard_peer); - cur_wireguard_peer = NULL; + g_array_append_val(npp->current.netdef->wireguard_peers, npp->current.wireguard_peer); + npp->current.wireguard_peer = NULL; } return TRUE; } @@ -2016,40 +1993,40 @@ handle_wireguard_peers(yaml_document_t* doc, yaml_node_t* node, const void* _, G ****************************************************/ static gboolean -handle_ovs_bond_lacp(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_ovs_bond_lacp(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - if (cur_netdef->type != NETPLAN_DEF_TYPE_BOND) - return yaml_error(node, error, "Key 'lacp' is only valid for interface type 'openvswitch bond'"); + if (npp->current.netdef->type != NETPLAN_DEF_TYPE_BOND) + return yaml_error(npp, node, error, "Key 'lacp' is only valid for interface type 'openvswitch bond'"); if (g_strcmp0(scalar(node), "active") && g_strcmp0(scalar(node), "passive") && g_strcmp0(scalar(node), "off")) - return yaml_error(node, error, "Value of 'lacp' needs to be 'active', 'passive' or 'off"); + return yaml_error(npp, node, error, "Value of 'lacp' needs to be 'active', 'passive' or 'off"); - return handle_netdef_str(doc, node, data, error); + return handle_netdef_str(npp, node, data, error); } static gboolean -handle_ovs_bridge_bool(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_ovs_bridge_bool(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - if (cur_netdef->type != NETPLAN_DEF_TYPE_BRIDGE) - return yaml_error(node, error, "Key is only valid for interface type 'openvswitch bridge'"); + if (npp->current.netdef->type != NETPLAN_DEF_TYPE_BRIDGE) + return yaml_error(npp, node, error, "Key is only valid for interface type 'openvswitch bridge'"); - return handle_netdef_bool(doc, node, data, error); + return handle_netdef_bool(npp, node, data, error); } static gboolean -handle_ovs_bridge_fail_mode(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_ovs_bridge_fail_mode(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - if (cur_netdef->type != NETPLAN_DEF_TYPE_BRIDGE) - return yaml_error(node, error, "Key 'fail-mode' is only valid for interface type 'openvswitch bridge'"); + if (npp->current.netdef->type != NETPLAN_DEF_TYPE_BRIDGE) + return yaml_error(npp, node, error, "Key 'fail-mode' is only valid for interface type 'openvswitch bridge'"); if (g_strcmp0(scalar(node), "standalone") && g_strcmp0(scalar(node), "secure")) - return yaml_error(node, error, "Value of 'fail-mode' needs to be 'standalone' or 'secure'"); + return yaml_error(npp, node, error, "Value of 'fail-mode' needs to be 'standalone' or 'secure'"); - return handle_netdef_str(doc, node, data, error); + return handle_netdef_str(npp, node, data, error); } static gboolean -handle_ovs_protocol(yaml_document_t* doc, yaml_node_t* node, void* entryptr, const void* data, GError** error) +handle_ovs_protocol(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error) { const char* supported[] = { "OpenFlow10", "OpenFlow11", "OpenFlow12", "OpenFlow13", "OpenFlow14", "OpenFlow15", "OpenFlow16", NULL @@ -2059,15 +2036,15 @@ handle_ovs_protocol(yaml_document_t* doc, yaml_node_t* node, void* entryptr, con GArray** protocols = (GArray**) ((void*) entryptr + offset); for (yaml_node_item_t *iter = node->data.sequence.items.start; iter < node->data.sequence.items.top; iter++) { - yaml_node_t *entry = yaml_document_get_node(doc, *iter); - assert_type(entry, YAML_SCALAR_NODE); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *iter); + assert_type(npp, entry, YAML_SCALAR_NODE); for (i = 0; supported[i] != NULL; ++i) if (!g_strcmp0(scalar(entry), supported[i])) break; if (supported[i] == NULL) - return yaml_error(node, error, "Unsupported OVS 'protocol' value: %s", scalar(entry)); + return yaml_error(npp, node, error, "Unsupported OVS 'protocol' value: %s", scalar(entry)); if (!*protocols) *protocols = g_array_new(FALSE, FALSE, sizeof(char*)); @@ -2079,31 +2056,31 @@ handle_ovs_protocol(yaml_document_t* doc, yaml_node_t* node, void* entryptr, con } static gboolean -handle_ovs_bridge_protocol(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_ovs_bridge_protocol(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - if (cur_netdef->type != NETPLAN_DEF_TYPE_BRIDGE) - return yaml_error(node, error, "Key 'protocols' is only valid for interface type 'openvswitch bridge'"); + if (npp->current.netdef->type != NETPLAN_DEF_TYPE_BRIDGE) + return yaml_error(npp, node, error, "Key 'protocols' is only valid for interface type 'openvswitch bridge'"); - return handle_ovs_protocol(doc, node, cur_netdef, data, error); + return handle_ovs_protocol(npp, node, npp->current.netdef, data, error); } static gboolean -handle_ovs_bridge_controller_connection_mode(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_ovs_bridge_controller_connection_mode(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - if (cur_netdef->type != NETPLAN_DEF_TYPE_BRIDGE) - return yaml_error(node, error, "Key 'controller.connection-mode' is only valid for interface type 'openvswitch bridge'"); + if (npp->current.netdef->type != NETPLAN_DEF_TYPE_BRIDGE) + return yaml_error(npp, node, error, "Key 'controller.connection-mode' is only valid for interface type 'openvswitch bridge'"); if (g_strcmp0(scalar(node), "in-band") && g_strcmp0(scalar(node), "out-of-band")) - return yaml_error(node, error, "Value of 'connection-mode' needs to be 'in-band' or 'out-of-band'"); + return yaml_error(npp, node, error, "Value of 'connection-mode' needs to be 'in-band' or 'out-of-band'"); - return handle_netdef_str(doc, node, data, error); + return handle_netdef_str(npp, node, data, error); } static gboolean -handle_ovs_bridge_controller_addresses(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_ovs_bridge_controller_addresses(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - if (cur_netdef->type != NETPLAN_DEF_TYPE_BRIDGE) - return yaml_error(node, error, "Key 'controller.addresses' is only valid for interface type 'openvswitch bridge'"); + if (npp->current.netdef->type != NETPLAN_DEF_TYPE_BRIDGE) + return yaml_error(npp, node, error, "Key 'controller.addresses' is only valid for interface type 'openvswitch bridge'"); for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { gchar** vec = NULL; @@ -2111,11 +2088,11 @@ handle_ovs_bridge_controller_addresses(yaml_document_t* doc, yaml_node_t* node, gboolean is_port = FALSE; gboolean is_unix = FALSE; - yaml_node_t *entry = yaml_document_get_node(doc, *i); - assert_type(entry, YAML_SCALAR_NODE); + yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); + assert_type(npp, entry, YAML_SCALAR_NODE); /* We always need at least one colon */ if (!g_strrstr(scalar(entry), ":")) - return yaml_error(node, error, "Unsupported OVS controller target: %s", scalar(entry)); + return yaml_error(npp, node, error, "Unsupported OVS controller target: %s", scalar(entry)); vec = g_strsplit (scalar(entry), ":", 2); @@ -2123,31 +2100,31 @@ handle_ovs_bridge_controller_addresses(yaml_document_t* doc, yaml_node_t* node, is_port = !g_strcmp0(vec[0], "ptcp") || !g_strcmp0(vec[0], "pssl"); is_unix = !g_strcmp0(vec[0], "unix") || !g_strcmp0(vec[0], "punix"); - if (!cur_netdef->ovs_settings.controller.addresses) - cur_netdef->ovs_settings.controller.addresses = g_array_new(FALSE, FALSE, sizeof(char*)); + if (!npp->current.netdef->ovs_settings.controller.addresses) + npp->current.netdef->ovs_settings.controller.addresses = g_array_new(FALSE, FALSE, sizeof(char*)); /* Format: [p]unix:file */ if (is_unix && vec[1] != NULL && vec[2] == NULL) { char* s = g_strdup(scalar(entry)); - g_array_append_val(cur_netdef->ovs_settings.controller.addresses, s); + g_array_append_val(npp->current.netdef->ovs_settings.controller.addresses, s); g_strfreev(vec); continue; /* Format tcp:host[:port] or ssl:host[:port] */ } else if (is_host && validate_ovs_target(TRUE, vec[1])) { char* s = g_strdup(scalar(entry)); - g_array_append_val(cur_netdef->ovs_settings.controller.addresses, s); + g_array_append_val(npp->current.netdef->ovs_settings.controller.addresses, s); g_strfreev(vec); continue; /* Format ptcp:[port][:host] or pssl:[port][:host] */ } else if (is_port && validate_ovs_target(FALSE, vec[1])) { char* s = g_strdup(scalar(entry)); - g_array_append_val(cur_netdef->ovs_settings.controller.addresses, s); + g_array_append_val(npp->current.netdef->ovs_settings.controller.addresses, s); g_strfreev(vec); continue; } g_strfreev(vec); - return yaml_error(node, error, "Unsupported OVS controller target: %s", scalar(entry)); + return yaml_error(npp, node, error, "Unsupported OVS controller target: %s", scalar(entry)); } return TRUE; @@ -2172,13 +2149,13 @@ static const mapping_entry_handler ovs_backend_settings_handlers[] = { }; static gboolean -handle_ovs_backend(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_ovs_backend(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { GList* values = NULL; - gboolean ret = process_mapping(doc, node, ovs_backend_settings_handlers, &values, error); + gboolean ret = process_mapping(npp, node, ovs_backend_settings_handlers, &values, error); guint len = g_list_length(values); - if (cur_netdef->type != NETPLAN_DEF_TYPE_BOND && cur_netdef->type != NETPLAN_DEF_TYPE_BRIDGE) { + if (npp->current.netdef->type != NETPLAN_DEF_TYPE_BOND && npp->current.netdef->type != NETPLAN_DEF_TYPE_BRIDGE) { GList *other_config = g_list_find_custom(values, "other-config", (GCompareFunc) strcmp); GList *external_ids = g_list_find_custom(values, "external-ids", (GCompareFunc) strcmp); /* Non-bond/non-bridge interfaces might still be handled by the networkd backend */ @@ -2192,7 +2169,7 @@ handle_ovs_backend(yaml_document_t* doc, yaml_node_t* node, const void* _, GErro /* Set the renderer for this device to NETPLAN_BACKEND_OVS, implicitly. * But only if empty "openvswitch: {}" or "openvswitch:" with more than * "other-config" or "external-ids" keys is given. */ - cur_netdef->backend = NETPLAN_BACKEND_OVS; + npp->current.netdef->backend = NETPLAN_BACKEND_OVS; return ret; } @@ -2356,37 +2333,37 @@ static const mapping_entry_handler tunnel_def_handlers[] = { ****************************************************/ static gboolean -handle_network_version(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_network_version(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { long mangled_version; mangled_version = strtol(scalar(node), NULL, 10); if (mangled_version < NETPLAN_VERSION_MIN || mangled_version >= NETPLAN_VERSION_MAX) - return yaml_error(node, error, "Only version 2 is supported"); + return yaml_error(npp, node, error, "Only version 2 is supported"); return TRUE; } static gboolean -handle_network_renderer(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_network_renderer(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { - return parse_renderer(node, &global_state.backend, error); + return parse_renderer(npp, node, &npp->global_backend, error); } static gboolean -handle_network_ovs_settings_global(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_network_ovs_settings_global(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - return handle_generic_map(doc, node, &ovs_settings_global, data, error); + return handle_generic_map(npp, node, &npp->global_ovs_settings, data, error); } static gboolean -handle_network_ovs_settings_global_protocol(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_network_ovs_settings_global_protocol(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { - return handle_ovs_protocol(doc, node, &ovs_settings_global, data, error); + return handle_ovs_protocol(npp, node, &npp->global_ovs_settings, data, error); } static gboolean -handle_network_ovs_settings_global_ports(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_network_ovs_settings_global_ports(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { yaml_node_t* port = NULL; yaml_node_t* peer = NULL; @@ -2395,44 +2372,44 @@ handle_network_ovs_settings_global_ports(yaml_document_t* doc, yaml_node_t* node NetplanNetDefinition *component = NULL; for (yaml_node_item_t *iter = node->data.sequence.items.start; iter < node->data.sequence.items.top; iter++) { - pair = yaml_document_get_node(doc, *iter); - assert_type(pair, YAML_SEQUENCE_NODE); + pair = yaml_document_get_node(&npp->doc, *iter); + assert_type(npp, pair, YAML_SEQUENCE_NODE); item = pair->data.sequence.items.start; /* A peer port definition must contain exactly 2 ports */ if (item+2 != pair->data.sequence.items.top) { - return yaml_error(pair, error, "An openvswitch peer port sequence must have exactly two entries"); + return yaml_error(npp, pair, error, "An openvswitch peer port sequence must have exactly two entries"); } - port = yaml_document_get_node(doc, *item); - assert_type(port, YAML_SCALAR_NODE); - peer = yaml_document_get_node(doc, *(item+1)); - assert_type(peer, YAML_SCALAR_NODE); + port = yaml_document_get_node(&npp->doc, *item); + assert_type(npp, port, YAML_SCALAR_NODE); + peer = yaml_document_get_node(&npp->doc, *(item+1)); + assert_type(npp, peer, YAML_SCALAR_NODE); /* Create port 1 netdef */ - component = netdefs ? g_hash_table_lookup(netdefs, scalar(port)) : NULL; + component = npp->parsed_defs ? g_hash_table_lookup(npp->parsed_defs, scalar(port)) : NULL; if (!component) { - component = netplan_netdef_new(scalar(port), NETPLAN_DEF_TYPE_PORT, NETPLAN_BACKEND_OVS); - if (g_hash_table_remove(missing_id, scalar(port))) - missing_ids_found++; + component = netplan_netdef_new(npp, scalar(port), NETPLAN_DEF_TYPE_PORT, NETPLAN_BACKEND_OVS); + if (g_hash_table_remove(npp->missing_id, scalar(port))) + npp->missing_ids_found++; } if (component->peer && g_strcmp0(component->peer, scalar(peer))) - return yaml_error(port, error, "openvswitch port '%s' is already assigned to peer '%s'", + return yaml_error(npp, port, error, "openvswitch port '%s' is already assigned to peer '%s'", component->id, component->peer); component->peer = g_strdup(scalar(peer)); /* Create port 2 (peer) netdef */ component = NULL; - component = netdefs ? g_hash_table_lookup(netdefs, scalar(peer)) : NULL; + component = npp->parsed_defs ? g_hash_table_lookup(npp->parsed_defs, scalar(peer)) : NULL; if (!component) { - component = netplan_netdef_new(scalar(peer), NETPLAN_DEF_TYPE_PORT, NETPLAN_BACKEND_OVS); - if (g_hash_table_remove(missing_id, scalar(peer))) - missing_ids_found++; + component = netplan_netdef_new(npp, scalar(peer), NETPLAN_DEF_TYPE_PORT, NETPLAN_BACKEND_OVS); + if (g_hash_table_remove(npp->missing_id, scalar(peer))) + npp->missing_ids_found++; } if (component->peer && g_strcmp0(component->peer, scalar(port))) - return yaml_error(peer, error, "openvswitch port '%s' is already assigned to peer '%s'", + return yaml_error(npp, peer, error, "openvswitch port '%s' is already assigned to peer '%s'", component->id, component->peer); component->peer = g_strdup(scalar(port)); } @@ -2444,53 +2421,53 @@ handle_network_ovs_settings_global_ports(yaml_document_t* doc, yaml_node_t* node * @data: netdef_type (as pointer) */ static gboolean -handle_network_type(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error) +handle_network_type(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error) { for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) { yaml_node_t* key, *value; const mapping_entry_handler* handlers; - key = yaml_document_get_node(doc, entry->key); - if (!assert_valid_id(key, error)) + key = yaml_document_get_node(&npp->doc, entry->key); + if (!assert_valid_id(npp, key, error)) return FALSE; /* globbing is not allowed for IDs */ if (strpbrk(scalar(key), "*[]?")) - return yaml_error(key, error, "Definition ID '%s' must not use globbing", scalar(key)); + return yaml_error(npp, key, error, "Definition ID '%s' must not use globbing", scalar(key)); - value = yaml_document_get_node(doc, entry->value); + value = yaml_document_get_node(&npp->doc, entry->value); /* special-case "renderer:" key to set the per-type backend */ if (strcmp(scalar(key), "renderer") == 0) { - if (!parse_renderer(value, &backend_cur_type, error)) + if (!parse_renderer(npp, value, &npp->current.backend, error)) return FALSE; continue; } - assert_type(value, YAML_MAPPING_NODE); + assert_type(npp, value, YAML_MAPPING_NODE); /* At this point we've seen a new starting definition, if it has been * already mentioned in another netdef, removing it from our "missing" * list. */ - if(g_hash_table_remove(missing_id, scalar(key))) - missing_ids_found++; + if(g_hash_table_remove(npp->missing_id, scalar(key))) + npp->missing_ids_found++; - cur_netdef = netdefs ? g_hash_table_lookup(netdefs, scalar(key)) : NULL; - if (cur_netdef) { + npp->current.netdef = npp->parsed_defs ? g_hash_table_lookup(npp->parsed_defs, scalar(key)) : NULL; + if (npp->current.netdef) { /* already exists, overriding/amending previous definition */ - if (cur_netdef->type != GPOINTER_TO_UINT(data)) - return yaml_error(key, error, "Updated definition '%s' changes device type", scalar(key)); + if (npp->current.netdef->type != GPOINTER_TO_UINT(data)) + return yaml_error(npp, key, error, "Updated definition '%s' changes device type", scalar(key)); } else { - cur_netdef = netplan_netdef_new(scalar(key), GPOINTER_TO_UINT(data), backend_cur_type); + npp->current.netdef = netplan_netdef_new(npp, scalar(key), GPOINTER_TO_UINT(data), npp->current.backend); } - g_assert(cur_filename); - cur_netdef->filename = g_strdup(cur_filename); + g_assert(npp->current.filename); + npp->current.netdef->filename = g_strdup(npp->current.filename); // XXX: breaks multi-pass parsing. - //if (!g_hash_table_add(ids_in_file, cur_netdef->id)) - // return yaml_error(key, error, "Duplicate net definition ID '%s'", cur_netdef->id); + //if (!g_hash_table_add(ids_in_file, npp->current.netdef->id)) + // return yaml_error(npp, key, error, "Duplicate net definition ID '%s'", npp->current.netdef->id); /* and fill it with definitions */ - switch (cur_netdef->type) { + switch (npp->current.netdef->type) { case NETPLAN_DEF_TYPE_BOND: handlers = bond_def_handlers; break; case NETPLAN_DEF_TYPE_BRIDGE: handlers = bridge_def_handlers; break; case NETPLAN_DEF_TYPE_ETHERNET: handlers = ethernet_def_handlers; break; @@ -2499,24 +2476,24 @@ handle_network_type(yaml_document_t* doc, yaml_node_t* node, const void* data, G case NETPLAN_DEF_TYPE_VLAN: handlers = vlan_def_handlers; break; case NETPLAN_DEF_TYPE_WIFI: handlers = wifi_def_handlers; break; case NETPLAN_DEF_TYPE_NM: - g_warning("netplan: %s: handling NetworkManager passthrough device, settings are not fully supported.", cur_netdef->id); + g_warning("netplan: %s: handling NetworkManager passthrough device, settings are not fully supported.", npp->current.netdef->id); handlers = ethernet_def_handlers; break; default: g_assert_not_reached(); // LCOV_EXCL_LINE } - if (!process_mapping(doc, value, handlers, NULL, error)) + if (!process_mapping(npp, value, handlers, NULL, error)) return FALSE; /* validate definition-level conditions */ - if (!validate_netdef_grammar(cur_netdef, value, error)) + if (!validate_netdef_grammar(npp, npp->current.netdef, value, error)) return FALSE; /* convenience shortcut: physical device without match: means match * name on ID */ - if (cur_netdef->type < NETPLAN_DEF_TYPE_VIRTUAL && !cur_netdef->has_match) - set_str_if_null(cur_netdef->match.original_name, cur_netdef->id); + if (npp->current.netdef->type < NETPLAN_DEF_TYPE_VIRTUAL && !npp->current.netdef->has_match) + set_str_if_null(npp->current.netdef->match.original_name, npp->current.netdef->id); } - backend_cur_type = NETPLAN_BACKEND_NONE; + npp->current.backend = NETPLAN_BACKEND_NONE; return TRUE; } @@ -2528,13 +2505,13 @@ static const mapping_entry_handler ovs_global_ssl_handlers[] = { }; static gboolean -handle_ovs_global_ssl(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error) +handle_ovs_global_ssl(NetplanParser* npp, yaml_node_t* node, const void* _, GError** error) { gboolean ret; - cur_auth = &(ovs_settings_global.ssl); - ret = process_mapping(doc, node, ovs_global_ssl_handlers, NULL, error); - cur_auth = NULL; + npp->current.auth = &(npp->global_ovs_settings.ssl); + ret = process_mapping(npp, node, ovs_global_ssl_handlers, NULL, error); + npp->current.auth = NULL; return ret; } @@ -2576,32 +2553,32 @@ static const mapping_entry_handler root_handlers[] = { * Handle multiple-pass parsing of the yaml document. */ static gboolean -process_document(yaml_document_t* doc, GError** error) +process_document(NetplanParser* npp, GError** error) { gboolean ret; int previously_found; int still_missing; - g_assert(missing_id == NULL); - missing_id = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); + g_assert(npp->missing_id == NULL); + npp->missing_id = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); do { g_debug("starting new processing pass"); - previously_found = missing_ids_found; - missing_ids_found = 0; + previously_found = npp->missing_ids_found; + npp->missing_ids_found = 0; g_clear_error(error); - ret = process_mapping(doc, yaml_document_get_root_node(doc), root_handlers, NULL, error); + ret = process_mapping(npp, yaml_document_get_root_node(&npp->doc), root_handlers, NULL, error); - still_missing = g_hash_table_size(missing_id); + still_missing = g_hash_table_size(npp->missing_id); - if (still_missing > 0 && missing_ids_found == previously_found) + if (still_missing > 0 && npp->missing_ids_found == previously_found) break; - } while (still_missing > 0 || missing_ids_found > 0); + } while (still_missing > 0 || npp->missing_ids_found > 0); - if (g_hash_table_size(missing_id) > 0) { + if (g_hash_table_size(npp->missing_id) > 0) { GHashTableIter iter; gpointer key, value; NetplanMissingNode *missing; @@ -2610,17 +2587,17 @@ process_document(yaml_document_t* doc, GError** error) /* Get the first missing identifier we can get from our list, to * approximate early failure and give the user a meaningful error. */ - g_hash_table_iter_init (&iter, missing_id); + g_hash_table_iter_init (&iter, npp->missing_id); g_hash_table_iter_next (&iter, &key, &value); missing = (NetplanMissingNode*) value; - return yaml_error(missing->node, error, "%s: interface '%s' is not defined", + return yaml_error(npp, missing->node, error, "%s: interface '%s' is not defined", missing->netdef_id, key); } - g_hash_table_destroy(missing_id); - missing_id = NULL; + g_hash_table_destroy(npp->missing_id); + npp->missing_id = NULL; return ret; } @@ -2628,107 +2605,163 @@ process_document(yaml_document_t* doc, GError** error) * Parse given YAML file and create/update global "netdefs" list. */ gboolean -netplan_parse_yaml(const char* filename, GError** error) +netplan_parser_load_yaml(NetplanParser* npp, const char* filename, GError** error) { - yaml_document_t doc; + yaml_document_t *doc = &npp->doc; gboolean ret; - if (!load_yaml(filename, &doc, error)) + if (!load_yaml(filename, doc, error)) return FALSE; /* empty file? */ - if (yaml_document_get_root_node(&doc) == NULL) + if (yaml_document_get_root_node(doc) == NULL) return TRUE; - g_assert(ids_in_file == NULL); - ids_in_file = g_hash_table_new(g_str_hash, NULL); + g_assert(npp->ids_in_file == NULL); + npp->ids_in_file = g_hash_table_new(g_str_hash, NULL); - cur_filename = filename; - ret = process_document(&doc, error); + npp->current.filename = g_strdup(filename); + ret = process_document(npp, error); + g_free((void *)npp->current.filename); + npp->current.filename = NULL; - cur_filename = NULL; - cur_netdef = NULL; - yaml_document_delete(&doc); - g_hash_table_destroy(ids_in_file); - ids_in_file = NULL; + yaml_document_delete(doc); + g_hash_table_destroy(npp->ids_in_file); + npp->ids_in_file = NULL; return ret; } static gboolean -finish_iterator(NetplanNetDefinition* nd, GError **error) +finish_iterator(const NetplanParser* npp, NetplanNetDefinition* nd, GError **error) { /* Take more steps to make sure we always have a backend set for netdefs */ if (nd->backend == NETPLAN_BACKEND_NONE) { - nd->backend = get_default_backend_for_type(nd->type); + nd->backend = get_default_backend_for_type(npp, nd->type); g_debug("%s: setting default backend to %i", nd->id, nd->backend); } /* Do a final pass of validation for backend-specific conditions */ - return validate_backend_rules(nd, error); + return validate_backend_rules(npp, nd, error); } -/** - * Post-processing after parsing all config files - */ -GHashTable * -netplan_finish_parse(GError** error) +static gboolean +insert_kv_into_hash(void *key, void *value, void *hash) +{ + g_hash_table_insert(hash, key, value); + return TRUE; +} + +gboolean +netplan_state_import_parser_results(NetplanState* np_state, NetplanParser* npp, GError** error) { - if (netdefs) { + if (npp->parsed_defs) { GError *recoverable = NULL; GHashTableIter iter; gpointer key, value; g_debug("We have some netdefs, pass them through a final round of validation"); - if (!validate_default_route_consistency(netdefs, &recoverable)) { + if (!validate_default_route_consistency(npp, npp->parsed_defs, &recoverable)) { g_warning("Problem encountered while validating default route consistency." "Please set up multiple routing tables and use `routing-policy` instead.\n" "Error: %s", (recoverable) ? recoverable->message : ""); g_clear_error(&recoverable); } - g_hash_table_iter_init (&iter, netdefs); + + g_hash_table_iter_init (&iter, npp->parsed_defs); while (g_hash_table_iter_next (&iter, &key, &value)) { - if (!finish_iterator((NetplanNetDefinition *) value, error)) - return NULL; + g_assert(np_state->netdefs == NULL || + g_hash_table_lookup(np_state->netdefs, key) == NULL); + if (!finish_iterator(npp, (NetplanNetDefinition *) value, error)) + return FALSE; g_debug("Configuration is valid"); } } - return netdefs; + + if (npp->parsed_defs) { + if (!np_state->netdefs) + np_state->netdefs = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_foreach_steal(npp->parsed_defs, insert_kv_into_hash, np_state->netdefs); + } + np_state->netdefs_ordered = g_list_concat(np_state->netdefs_ordered, npp->ordered); + np_state->ovs_settings = npp->global_ovs_settings; + np_state->backend = npp->global_backend; + + /* We need to reset those fields manually as we transfered ownership of the underlying + data to out. If we don't do this, netplan_clear_parser will deallocate data + that we don't own anymore. */ + npp->parsed_defs = NULL; + npp->ordered = NULL; + memset(&npp->global_ovs_settings, 0, sizeof(NetplanOVSSettings)); + + netplan_parser_reset(npp); + return TRUE; +} + +static void +clear_netdef_from_list(void *def) +{ + reset_netdef((NetplanNetDefinition *)def, NETPLAN_DEF_TYPE_NONE, NETPLAN_BACKEND_NONE); + g_free(def); +} + +NetplanParser* +netplan_parser_new() +{ + NetplanParser* npp = g_new0(NetplanParser, 1); + netplan_parser_reset(npp); + return npp; } void -process_input_file(const char* f) +netplan_parser_reset(NetplanParser* npp) { - GError* error = NULL; + g_assert(npp != NULL); + + if(npp->parsed_defs) { + /* FIXME: make sure that any dynamically allocated netdef data is freed */ + g_hash_table_destroy(npp->parsed_defs); + npp->parsed_defs = NULL; + } + if(npp->ordered) { + g_clear_list(&npp->ordered, clear_netdef_from_list); + npp->ordered = NULL; + } + + npp->global_backend = NETPLAN_BACKEND_NONE; + reset_ovs_settings(&npp->global_ovs_settings); + + /* These pointers are non-owning, it's not our place to free their resources*/ + npp->current.netdef = NULL; + npp->current.auth = NULL; + + access_point_clear(&npp->current.access_point, npp->current.backend); + wireguard_peer_clear(&npp->current.wireguard_peer); + address_options_clear(&npp->current.addr_options); + route_clear(&npp->current.route); + ip_rule_clear(&npp->current.ip_rule); + g_free((void *)npp->current.filename); + npp->current.filename = NULL; + + //LCOV_EXCL_START + if (npp->ids_in_file) { + g_hash_table_destroy(npp->ids_in_file); + npp->ids_in_file = NULL; + } + //LCOV_EXCL_STOP - g_debug("Processing input file %s..", f); - if (!netplan_parse_yaml(f, &error)) { - g_fprintf(stderr, "%s\n", error->message); - exit(1); + if (npp->missing_id) { + g_hash_table_destroy(npp->missing_id); + npp->missing_id = NULL; } + + npp->missing_ids_found = 0; } -gboolean -process_yaml_hierarchy(const char* rootdir) -{ - glob_t gl; - /* Files with asciibetically higher names override/append settings from - * earlier ones (in all config dirs); files in /run/netplan/ - * shadow files in /etc/netplan/ which shadow files in /lib/netplan/. - * To do that, we put all found files in a hash table, then sort it by - * file name, and add the entries from /run after the ones from /etc - * and those after the ones from /lib. */ - if (find_yaml_glob(rootdir, &gl) != 0) - return FALSE; // LCOV_EXCL_LINE - /* keys are strdup()ed, free them; values point into the glob_t, don't free them */ - g_autoptr(GHashTable) configs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - g_autoptr(GList) config_keys = NULL; - - for (size_t i = 0; i < gl.gl_pathc; ++i) - g_hash_table_insert(configs, g_path_get_basename(gl.gl_pathv[i]), gl.gl_pathv[i]); - - config_keys = g_list_sort(g_hash_table_get_keys(configs), (GCompareFunc) strcmp); - - for (GList* i = config_keys; i != NULL; i = i->next) - process_input_file(g_hash_table_lookup(configs, i->data)); - return TRUE; +void +netplan_parser_clear(NetplanParser** npp_p) +{ + NetplanParser* npp = *npp_p; + *npp_p = NULL; + netplan_parser_reset(npp); + g_free(npp); } diff --git a/src/types.c b/src/types.c index a372bb4..b8c967d 100644 --- a/src/types.c +++ b/src/types.c @@ -113,7 +113,7 @@ reset_auth_settings(NetplanAuthenticationSettings* auth) auth->eap_method = NETPLAN_AUTH_EAP_NONE; } -static void +void reset_ovs_settings(NetplanOVSSettings* settings) { settings->mcast_snooping = FALSE; @@ -331,19 +331,22 @@ clear_netdef_from_list(void *def) g_free(def); } -// LCOV_EXCL_START NetplanState* netplan_state_new() { - return NULL; + NetplanState* np_state = g_new0(NetplanState, 1); + netplan_state_reset(np_state); + return np_state; } void netplan_state_clear(NetplanState** np_state_p) { - g_assert(0); + NetplanState* np_state = *np_state_p; + *np_state_p = NULL; + netplan_state_reset(np_state); + g_free(np_state); } -// LCOV_EXCL_STOP void netplan_state_reset(NetplanState* np_state) @@ -380,3 +383,43 @@ netplan_state_get_netdefs_size(const NetplanState* np_state) { return np_state->netdefs ? g_hash_table_size(np_state->netdefs) : 0; } + +void +access_point_clear(NetplanWifiAccessPoint** ap, NetplanBackend backend) +{ + NetplanWifiAccessPoint* obj = *ap; + if (!obj) + return; + *ap = NULL; + free_access_point(NULL, obj, &backend); +} + +#define CLEAR_FROM_FREE(free_fn, clear_fn, type) void clear_fn(type** dest) \ +{ \ + type* obj; \ + if (!dest || !(*dest)) return; \ + obj = *dest; \ + *dest = NULL; \ + free_fn(obj);\ +} + +CLEAR_FROM_FREE(free_wireguard_peer, wireguard_peer_clear, NetplanWireguardPeer); +CLEAR_FROM_FREE(free_ip_rules, ip_rule_clear, NetplanIPRule); +CLEAR_FROM_FREE(free_route, route_clear, NetplanIPRoute); +CLEAR_FROM_FREE(free_address_options, address_options_clear, NetplanAddressOptions); + +NetplanNetDefinition* +netplan_state_get_netdef(const NetplanState* np_state, const char* id) +{ + if (!np_state->netdefs) + return NULL; + return g_hash_table_lookup(np_state->netdefs, id); +} + +NETPLAN_PUBLIC const char * +netplan_netdef_get_filename(const NetplanNetDefinition* netdef) +{ + if (!netdef) + return NULL; + return netdef->filename; +} diff --git a/src/types.h b/src/types.h index 6e4ec7f..8ed6400 100644 --- a/src/types.h +++ b/src/types.h @@ -431,6 +431,57 @@ struct netplan_state { NetplanOVSSettings ovs_settings; }; +struct netplan_parser { + yaml_document_t doc; + /* Netplan definitions that have already been processed. + * Weak references to the nedefs */ + GHashTable* parsed_defs; + /* Same definitions, stored in the order of processing. + * Owning structure for the netdefs */ + GList* ordered; + NetplanBackend global_backend; + NetplanOVSSettings global_ovs_settings; + + /* Data currently being processed */ + struct { + /* Refs to objects allocated elsewhere */ + NetplanNetDefinition* netdef; + NetplanAuthenticationSettings *auth; + + /* Owned refs, not yet referenced anywhere */ + NetplanWifiAccessPoint *access_point; + NetplanWireguardPeer* wireguard_peer; + NetplanAddressOptions* addr_options; + NetplanIPRoute* route; + NetplanIPRule* ip_rule; + const char *filename; + + /* Plain old data representing the backend for which we are + * currently parsing. Not necessarily the same as the global + * backend. */ + NetplanBackend backend; + } current; + + /* List of "seen" ids not found in netdefs yet by the parser. + * These are removed when it exists in this list and we reach the point of + * creating a netdef for that id; so by the time we're done parsing the yaml + * document it should be empty. + * + * Keys are not owned, but the values are. Should be created with NULL and g_free + * destructors, respectively, so that the cleanup is automatic at destruction. + */ + GHashTable* missing_id; + + /* Set of IDs in currently parsed YAML file, for being able to detect + * "duplicate ID within one file" vs. allowing a drop-in to override/amend an + * existing definition. + * + * Appears to be unused? + * */ + GHashTable* ids_in_file; + int missing_ids_found; +}; + #define NETPLAN_ADVERTISED_RECEIVE_WINDOW_UNSPEC 0 #define NETPLAN_CONGESTION_WINDOW_UNSPEC 0 #define NETPLAN_MTU_UNSPEC 0 @@ -441,4 +492,22 @@ struct netplan_state { #define NETPLAN_IP_RULE_TOS_UNSPEC G_MAXUINT void -reset_netdef(NetplanNetDefinition *netdef, NetplanDefType type, NetplanBackend renderer); +reset_netdef(NetplanNetDefinition* netdef, NetplanDefType type, NetplanBackend renderer); + +void +reset_ovs_settings(NetplanOVSSettings *settings); + +void +access_point_clear(NetplanWifiAccessPoint** ap, NetplanBackend backend); + +void +wireguard_peer_clear(NetplanWireguardPeer** peer); + +void +address_options_clear(NetplanAddressOptions** options); + +void +ip_rule_clear(NetplanIPRule** rule); + +void +route_clear(NetplanIPRoute** route); diff --git a/src/util-internal.h b/src/util-internal.h index a0068ca..7c79481 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -66,7 +66,13 @@ const char* tunnel_mode_to_string(NetplanTunnelMode mode); NetplanNetDefinition* -netplan_netdef_new(const char* id, NetplanDefType type, NetplanBackend renderer); +netplan_netdef_new(NetplanParser* npp, const char* id, NetplanDefType type, NetplanBackend renderer); + +const char * +netplan_parser_get_filename(NetplanParser* npp); + +NETPLAN_INTERNAL gboolean +netplan_parser_load_yaml_hierarchy(NetplanParser* npp, const char* rootdir, GError** error); NETPLAN_INTERNAL void process_input_file(const char* f); @@ -218,30 +218,40 @@ netplan_delete_connection(const char* id, const char* rootdir) g_autofree gchar* del = NULL; g_autoptr(GError) error = NULL; NetplanNetDefinition* nd = NULL; + gboolean ret = TRUE; + + NetplanState* np_state = netplan_state_new(); + NetplanParser* npp = netplan_parser_new(); /* parse all YAML files */ - if (!process_yaml_hierarchy(rootdir)) - return FALSE; // LCOV_EXCL_LINE + if ( !netplan_parser_load_yaml_hierarchy(npp, rootdir, &error) + || !netplan_state_import_parser_results(np_state, npp, &error)) { + // LCOV_EXCL_START + g_fprintf(stderr, "%s\n", error->message); + ret = FALSE; + goto cleanup; + // LCOV_EXCL_STOP + } - netdefs = netplan_finish_parse(&error); - if (!netdefs) { + if (!np_state->netdefs) { // LCOV_EXCL_START g_fprintf(stderr, "netplan_delete_connection: %s\n", error->message); - return FALSE; + ret = FALSE; + goto cleanup; // LCOV_EXCL_STOP } /* find filename for specified netdef ID */ - nd = g_hash_table_lookup(netdefs, id); + nd = g_hash_table_lookup(np_state->netdefs, id); if (!nd) { g_warning("netplan_delete_connection: Cannot delete %s, does not exist.", id); - return FALSE; + ret = FALSE; + goto cleanup; } filename = g_path_get_basename(nd->filename); filename[strlen(filename) - 5] = '\0'; //stip ".yaml" suffix del = g_strdup_printf("network.%s.%s=NULL", netplan_def_type_name(nd->type), id); - netplan_clear_netdefs(); /* TODO: refactor logic to actually be inside the library instead of spawning another process */ const gchar *argv[] = { SBINDIR "/" "netplan", "set", del, "--origin-hint" , filename, NULL, NULL, NULL }; @@ -251,7 +261,12 @@ netplan_delete_connection(const char* id, const char* rootdir) } if (getenv("TEST_NETPLAN_CMD") != 0) argv[0] = getenv("TEST_NETPLAN_CMD"); - return g_spawn_sync(NULL, (gchar**)argv, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL); + ret = g_spawn_sync(NULL, (gchar**)argv, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL); + +cleanup: + if (npp) netplan_parser_clear(&npp); + if (np_state) netplan_state_clear(&np_state); + return ret; } gboolean @@ -302,27 +317,31 @@ netplan_get_id_from_nm_filename(const char* filename, const char* ssid) return g_strndup(start, id_len); } -/** - * Get the filename from which the given netdef has been parsed. - * @rootdir: ID of the netdef to be looked up - * @rootdir: parse files from this root directory - */ -gchar* -netplan_get_filename_by_id(const char* netdef_id, const char* rootdir) +gboolean +netplan_parser_load_yaml_hierarchy(NetplanParser* npp, const char* rootdir, GError** error) { - gchar* filename = NULL; - netplan_clear_netdefs(); - if (!process_yaml_hierarchy(rootdir)) - return NULL; // LCOV_EXCL_LINE - GHashTable* netdefs = netplan_finish_parse(NULL); - if (!netdefs) - return NULL; - NetplanNetDefinition* nd = g_hash_table_lookup(netdefs, netdef_id); - if (!nd) - return NULL; - filename = g_strdup(nd->filename); - netplan_clear_netdefs(); - return filename; + glob_t gl; + /* Files with asciibetically higher names override/append settings from + * earlier ones (in all config dirs); files in /run/netplan/ + * shadow files in /etc/netplan/ which shadow files in /lib/netplan/. + * To do that, we put all found files in a hash table, then sort it by + * file name, and add the entries from /run after the ones from /etc + * and those after the ones from /lib. */ + if (find_yaml_glob(rootdir, &gl) != 0) + return FALSE; // LCOV_EXCL_LINE + /* keys are strdup()ed, free them; values point into the glob_t, don't free them */ + g_autoptr(GHashTable) configs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + g_autoptr(GList) config_keys = NULL; + + for (size_t i = 0; i < gl.gl_pathc; ++i) + g_hash_table_insert(configs, g_path_get_basename(gl.gl_pathv[i]), gl.gl_pathv[i]); + + config_keys = g_list_sort(g_hash_table_get_keys(configs), (GCompareFunc) strcmp); + + for (GList* i = config_keys; i != NULL; i = i->next) + if (!netplan_parser_load_yaml(npp, g_hash_table_lookup(configs, i->data), error)) + return FALSE; + return TRUE; } /** diff --git a/src/validation.c b/src/validation.c index 7b80559..2e172c7 100644 --- a/src/validation.c +++ b/src/validation.c @@ -155,7 +155,7 @@ validate_ovs_target(gboolean host_first, gchar* s) { * Validation for grammar and backend rules. ************************************************/ static gboolean -validate_tunnel_key(yaml_node_t* node, gchar* key, GError** error) +validate_tunnel_key(const NetplanParser* npp, yaml_node_t* node, gchar* key, GError** error) { /* Tunnel key should be a number or dotted quad, except for wireguard. */ gchar* endptr; @@ -163,53 +163,53 @@ validate_tunnel_key(yaml_node_t* node, gchar* key, GError** error) if (*endptr != '\0' || v > G_MAXUINT) { /* Not a simple uint, try for a dotted quad */ if (!is_ip4_address(key)) - return yaml_error(node, error, "invalid tunnel key '%s'", key); + return yaml_error(npp, node, error, "invalid tunnel key '%s'", key); } return TRUE; } static gboolean -validate_tunnel_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** error) +validate_tunnel_grammar(const NetplanParser* npp, NetplanNetDefinition* nd, yaml_node_t* node, GError** error) { if (nd->tunnel.mode == NETPLAN_TUNNEL_MODE_UNKNOWN) - return yaml_error(node, error, "%s: missing 'mode' property for tunnel", nd->id); + return yaml_error(npp, node, error, "%s: missing 'mode' property for tunnel", nd->id); if (nd->tunnel.mode == NETPLAN_TUNNEL_MODE_WIREGUARD) { if (!nd->tunnel.private_key) - return yaml_error(node, error, "%s: missing 'key' property (private key) for wireguard", nd->id); + return yaml_error(npp, node, error, "%s: missing 'key' property (private key) for wireguard", nd->id); if (nd->tunnel.private_key[0] != '/' && !is_wireguard_key(nd->tunnel.private_key)) - return yaml_error(node, error, "%s: invalid wireguard private key", nd->id); + return yaml_error(npp, node, error, "%s: invalid wireguard private key", nd->id); if (!nd->wireguard_peers || nd->wireguard_peers->len == 0) - return yaml_error(node, error, "%s: at least one peer is required.", nd->id); + return yaml_error(npp, node, error, "%s: at least one peer is required.", nd->id); for (guint i = 0; i < nd->wireguard_peers->len; i++) { NetplanWireguardPeer *peer = g_array_index (nd->wireguard_peers, NetplanWireguardPeer*, i); if (!peer->public_key) - return yaml_error(node, error, "%s: keys.public is required.", nd->id); + return yaml_error(npp, node, error, "%s: keys.public is required.", nd->id); if (!is_wireguard_key(peer->public_key)) - return yaml_error(node, error, "%s: invalid wireguard public key", nd->id); + return yaml_error(npp, node, error, "%s: invalid wireguard public key", nd->id); if (peer->preshared_key && peer->preshared_key[0] != '/' && !is_wireguard_key(peer->preshared_key)) - return yaml_error(node, error, "%s: invalid wireguard shared key", nd->id); + return yaml_error(npp, node, error, "%s: invalid wireguard shared key", nd->id); if (!peer->allowed_ips || peer->allowed_ips->len == 0) - return yaml_error(node, error, "%s: 'to' is required to define the allowed IPs.", nd->id); + return yaml_error(npp, node, error, "%s: 'to' is required to define the allowed IPs.", nd->id); if (peer->keepalive > 65535) - return yaml_error(node, error, "%s: keepalive must be 0-65535 inclusive.", nd->id); + return yaml_error(npp, node, error, "%s: keepalive must be 0-65535 inclusive.", nd->id); } return TRUE; } else { - if (nd->tunnel.input_key && !validate_tunnel_key(node, nd->tunnel.input_key, error)) + if (nd->tunnel.input_key && !validate_tunnel_key(npp, node, nd->tunnel.input_key, error)) return FALSE; - if (nd->tunnel.output_key && !validate_tunnel_key(node, nd->tunnel.output_key, error)) + if (nd->tunnel.output_key && !validate_tunnel_key(npp, node, nd->tunnel.output_key, error)) return FALSE; } /* Validate local/remote IPs */ if (!nd->tunnel.local_ip) - return yaml_error(node, error, "%s: missing 'local' property for tunnel", nd->id); + return yaml_error(npp, node, error, "%s: missing 'local' property for tunnel", nd->id); if (!nd->tunnel.remote_ip) - return yaml_error(node, error, "%s: missing 'remote' property for tunnel", nd->id); + return yaml_error(npp, node, error, "%s: missing 'remote' property for tunnel", nd->id); if (nd->tunnel_ttl && nd->tunnel_ttl > 255) - return yaml_error(node, error, "%s: 'ttl' property for tunnel must be in range [1...255]", nd->id); + return yaml_error(npp, node, error, "%s: 'ttl' property for tunnel must be in range [1...255]", nd->id); switch(nd->tunnel.mode) { case NETPLAN_TUNNEL_MODE_IPIP6: @@ -218,16 +218,16 @@ validate_tunnel_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** er case NETPLAN_TUNNEL_MODE_IP6GRETAP: case NETPLAN_TUNNEL_MODE_VTI6: if (!is_ip6_address(nd->tunnel.local_ip)) - return yaml_error(node, error, "%s: 'local' must be a valid IPv6 address for this tunnel type", nd->id); + return yaml_error(npp, node, error, "%s: 'local' must be a valid IPv6 address for this tunnel type", nd->id); if (!is_ip6_address(nd->tunnel.remote_ip)) - return yaml_error(node, error, "%s: 'remote' must be a valid IPv6 address for this tunnel type", nd->id); + return yaml_error(npp, node, error, "%s: 'remote' must be a valid IPv6 address for this tunnel type", nd->id); break; default: if (!is_ip4_address(nd->tunnel.local_ip)) - return yaml_error(node, error, "%s: 'local' must be a valid IPv4 address for this tunnel type", nd->id); + return yaml_error(npp, node, error, "%s: 'local' must be a valid IPv4 address for this tunnel type", nd->id); if (!is_ip4_address(nd->tunnel.remote_ip)) - return yaml_error(node, error, "%s: 'remote' must be a valid IPv4 address for this tunnel type", nd->id); + return yaml_error(npp, node, error, "%s: 'remote' must be a valid IPv4 address for this tunnel type", nd->id); break; } @@ -235,7 +235,7 @@ validate_tunnel_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** er } static gboolean -validate_tunnel_backend_rules(NetplanNetDefinition* nd, yaml_node_t* node, GError** error) +validate_tunnel_backend_rules(const NetplanParser* npp, NetplanNetDefinition* nd, yaml_node_t* node, GError** error) { /* Backend-specific validation rules for tunnels */ switch (nd->backend) { @@ -251,7 +251,7 @@ validate_tunnel_backend_rules(NetplanNetDefinition* nd, yaml_node_t* node, GErro * systemd-networkd has grown ISATAP support in 918049a. */ case NETPLAN_TUNNEL_MODE_ISATAP: - return yaml_error(node, error, + return yaml_error(npp, node, error, "%s: %s tunnel mode is not supported by networkd", nd->id, g_ascii_strup(netplan_tunnel_mode_name(nd->tunnel.mode), -1)); @@ -259,9 +259,9 @@ validate_tunnel_backend_rules(NetplanNetDefinition* nd, yaml_node_t* node, GErro default: if (nd->tunnel.input_key) - return yaml_error(node, error, "%s: 'input-key' is not required for this tunnel type", nd->id); + return yaml_error(npp, node, error, "%s: 'input-key' is not required for this tunnel type", nd->id); if (nd->tunnel.output_key) - return yaml_error(node, error, "%s: 'output-key' is not required for this tunnel type", nd->id); + return yaml_error(npp, node, error, "%s: 'output-key' is not required for this tunnel type", nd->id); break; } break; @@ -275,7 +275,7 @@ validate_tunnel_backend_rules(NetplanNetDefinition* nd, yaml_node_t* node, GErro case NETPLAN_TUNNEL_MODE_GRETAP: case NETPLAN_TUNNEL_MODE_IP6GRETAP: - return yaml_error(node, error, + return yaml_error(npp, node, error, "%s: %s tunnel mode is not supported by NetworkManager", nd->id, g_ascii_strup(netplan_tunnel_mode_name(nd->tunnel.mode), -1)); @@ -283,9 +283,9 @@ validate_tunnel_backend_rules(NetplanNetDefinition* nd, yaml_node_t* node, GErro default: if (nd->tunnel.input_key) - return yaml_error(node, error, "%s: 'input-key' is not required for this tunnel type", nd->id); + return yaml_error(npp, node, error, "%s: 'input-key' is not required for this tunnel type", nd->id); if (nd->tunnel.output_key) - return yaml_error(node, error, "%s: 'output-key' is not required for this tunnel type", nd->id); + return yaml_error(npp, node, error, "%s: 'output-key' is not required for this tunnel type", nd->id); break; } break; @@ -297,9 +297,9 @@ validate_tunnel_backend_rules(NetplanNetDefinition* nd, yaml_node_t* node, GErro } gboolean -validate_netdef_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** error) +validate_netdef_grammar(const NetplanParser* npp, NetplanNetDefinition* nd, yaml_node_t* node, GError** error) { - int missing_id_count = g_hash_table_size(missing_id); + int missing_id_count = g_hash_table_size(npp->missing_id); gboolean valid = FALSE; g_assert(nd->type != NETPLAN_DEF_TYPE_NONE); @@ -312,41 +312,41 @@ validate_netdef_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** er /* set-name: requires match: */ if (nd->set_name && !nd->has_match) - return yaml_error(node, error, "%s: 'set-name:' requires 'match:' properties", nd->id); + return yaml_error(npp, node, error, "%s: 'set-name:' requires 'match:' properties", nd->id); if (nd->type == NETPLAN_DEF_TYPE_WIFI && nd->access_points == NULL) - return yaml_error(node, error, "%s: No access points defined", nd->id); + return yaml_error(npp, node, error, "%s: No access points defined", nd->id); if (nd->type == NETPLAN_DEF_TYPE_VLAN) { if (!nd->vlan_link) - return yaml_error(node, error, "%s: missing 'link' property", nd->id); + return yaml_error(npp, node, error, "%s: missing 'link' property", nd->id); nd->vlan_link->has_vlans = TRUE; if (nd->vlan_id == G_MAXUINT) - return yaml_error(node, error, "%s: missing 'id' property", nd->id); + return yaml_error(npp, node, error, "%s: missing 'id' property", nd->id); if (nd->vlan_id > 4094) - return yaml_error(node, error, "%s: invalid id '%u' (allowed values are 0 to 4094)", nd->id, nd->vlan_id); + return yaml_error(npp, node, error, "%s: invalid id '%u' (allowed values are 0 to 4094)", nd->id, nd->vlan_id); } if (nd->type == NETPLAN_DEF_TYPE_TUNNEL) { - valid = validate_tunnel_grammar(nd, node, error); + valid = validate_tunnel_grammar(npp, nd, node, error); if (!valid) goto netdef_grammar_error; } if (nd->ip6_addr_gen_mode != NETPLAN_ADDRGEN_DEFAULT && nd->ip6_addr_gen_token) - return yaml_error(node, error, "%s: ipv6-address-generation and ipv6-address-token are mutually exclusive", nd->id); + return yaml_error(npp, node, error, "%s: ipv6-address-generation and ipv6-address-token are mutually exclusive", nd->id); if (nd->backend == NETPLAN_BACKEND_OVS) { // LCOV_EXCL_START if (!g_file_test(OPENVSWITCH_OVS_VSCTL, G_FILE_TEST_EXISTS)) { /* Tested via integration test */ - return yaml_error(node, error, "%s: The 'ovs-vsctl' tool is required to setup OpenVSwitch interfaces.", nd->id); + return yaml_error(npp, node, error, "%s: The 'ovs-vsctl' tool is required to setup OpenVSwitch interfaces.", nd->id); } // LCOV_EXCL_STOP } if (nd->type == NETPLAN_DEF_TYPE_NM && (!nd->backend_settings.nm.passthrough || !g_datalist_get_data(&nd->backend_settings.nm.passthrough, "connection.type"))) - return yaml_error(node, error, "%s: network type 'nm-devices:' needs to provide a 'connection.type' via passthrough", nd->id); + return yaml_error(npp, node, error, "%s: network type 'nm-devices:' needs to provide a 'connection.type' via passthrough", nd->id); valid = TRUE; @@ -355,7 +355,7 @@ netdef_grammar_error: } gboolean -validate_backend_rules(NetplanNetDefinition* nd, GError** error) +validate_backend_rules(const NetplanParser* npp, NetplanNetDefinition* nd, GError** error) { gboolean valid = FALSE; /* Set a dummy, NULL yaml_node_t for error reporting */ @@ -364,7 +364,7 @@ validate_backend_rules(NetplanNetDefinition* nd, GError** error) g_assert(nd->type != NETPLAN_DEF_TYPE_NONE); if (nd->type == NETPLAN_DEF_TYPE_TUNNEL) { - valid = validate_tunnel_backend_rules(nd, node, error); + valid = validate_tunnel_backend_rules(npp, nd, node, error); if (!valid) goto backend_rules_error; } @@ -437,7 +437,7 @@ check_defroute(struct _defroute_entry *candidate, } gboolean -validate_default_route_consistency(GHashTable *netdefs, GError ** error) +validate_default_route_consistency(const NetplanParser* npp, GHashTable *netdefs, GError ** error) { struct _defroute_entry candidate = {}; GSList *defroutes = NULL; diff --git a/src/validation.h b/src/validation.h index 18e98fe..e0a30f3 100644 --- a/src/validation.h +++ b/src/validation.h @@ -31,10 +31,10 @@ NETPLAN_ABI gboolean is_wireguard_key(const char* hostname); gboolean -validate_netdef_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** error); +validate_netdef_grammar(const NetplanParser* npp, NetplanNetDefinition* nd, yaml_node_t* node, GError** error); gboolean -validate_backend_rules(NetplanNetDefinition* nd, GError** error); +validate_backend_rules(const NetplanParser* npp, NetplanNetDefinition* nd, GError** error); gboolean -validate_default_route_consistency(GHashTable* netdefs, GError** error); +validate_default_route_consistency(const NetplanParser* npp, GHashTable* netdefs, GError** error); diff --git a/src/yaml-helpers.h b/src/yaml-helpers.h index 6408ac1..55fc94c 100644 --- a/src/yaml-helpers.h +++ b/src/yaml-helpers.h @@ -22,33 +22,33 @@ #define YAML_MAPPING_OPEN(event_ptr, emitter_ptr) \ { \ yaml_mapping_start_event_initialize(event_ptr, NULL, (yaml_char_t *)YAML_MAP_TAG, 1, YAML_BLOCK_MAPPING_STYLE); \ - if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto error; \ + if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \ } #define YAML_MAPPING_CLOSE(event_ptr, emitter_ptr) \ { \ yaml_mapping_end_event_initialize(event_ptr); \ - if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto error; \ + if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \ } #define YAML_SEQUENCE_OPEN(event_ptr, emitter_ptr) \ { \ yaml_sequence_start_event_initialize(event_ptr, NULL, (yaml_char_t *)YAML_SEQ_TAG, 1, YAML_BLOCK_SEQUENCE_STYLE); \ - if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto error; \ + if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \ } #define YAML_SEQUENCE_CLOSE(event_ptr, emitter_ptr) \ { \ yaml_sequence_end_event_initialize(event_ptr); \ - if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto error; \ + if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \ } #define YAML_SCALAR_PLAIN(event_ptr, emitter_ptr, scalar) \ { \ yaml_scalar_event_initialize(event_ptr, NULL, (yaml_char_t *)YAML_STR_TAG, (yaml_char_t *)scalar, strlen(scalar), 1, 0, YAML_PLAIN_SCALAR_STYLE); \ - if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto error; \ + if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \ } /* Implicit plain and quoted tags, double quoted style */ #define YAML_SCALAR_QUOTED(event_ptr, emitter_ptr, scalar) \ { \ yaml_scalar_event_initialize(event_ptr, NULL, (yaml_char_t *)YAML_STR_TAG, (yaml_char_t *)scalar, strlen(scalar), 1, 1, YAML_DOUBLE_QUOTED_SCALAR_STYLE); \ - if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto error; \ + if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \ } #define YAML_STRING(event_ptr, emitter_ptr, key, value_ptr) \ { \ @@ -77,9 +77,9 @@ yaml_emitter_initialize(emitter_ptr); \ yaml_emitter_set_output_file(emitter_ptr, file); \ yaml_stream_start_event_initialize(event_ptr, YAML_UTF8_ENCODING); \ - if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto error; \ + if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \ yaml_document_start_event_initialize(event_ptr, NULL, NULL, NULL, 1); \ - if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto error; \ + if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \ YAML_MAPPING_OPEN(event_ptr, emitter_ptr); \ } /* close initial YAML mapping, document, stream and emitter */ @@ -87,8 +87,8 @@ { \ YAML_MAPPING_CLOSE(event_ptr, emitter_ptr); \ yaml_document_end_event_initialize(event_ptr, 1); \ - if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto error; \ + if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \ yaml_stream_end_event_initialize(event_ptr); \ - if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto error; \ + if (!yaml_emitter_emit(emitter_ptr, event_ptr)) goto err_path; \ yaml_emitter_delete(emitter_ptr); \ } |