summary refs log tree commit diff
path: root/src/initrd/nmi-cmdline-reader.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/initrd/nmi-cmdline-reader.c')
-rw-r--r--src/initrd/nmi-cmdline-reader.c556
1 files changed, 336 insertions, 220 deletions
diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c
index 2cb93a2d..2eb6d7e5 100644
--- a/src/initrd/nmi-cmdline-reader.c
+++ b/src/initrd/nmi-cmdline-reader.c
@@ -17,33 +17,67 @@
 
 /*****************************************************************************/
 
-static gboolean
-_connection_matches_type (gpointer key, gpointer value, gpointer user_data)
+typedef struct {
+	GHashTable *hash;
+	GPtrArray *array;
+	NMConnection *bootdev_connection;   /* connection for bootdev=$ifname */
+	NMConnection *default_connection;   /* connection not bound to any ifname */
+} Reader;
+
+static Reader *
+reader_new (void)
 {
-	NMConnection *connection = value;
-	const char *type_name = user_data;
-	NMSettingConnection *s_con;
+	Reader *reader;
 
-	s_con = nm_connection_get_setting_connection (connection);
-	if (type_name == NULL)
-		return nm_setting_connection_get_master (s_con) == NULL;
-	else
-		return strcmp (nm_setting_connection_get_connection_type (s_con), type_name) == 0;
+	reader = g_slice_new (Reader);
+	*reader = (Reader) {
+		.hash  = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref),
+		.array = g_ptr_array_new (),
+	};
+
+	return reader;
+}
+
+static GHashTable *
+reader_destroy (Reader *reader, gboolean free_hash)
+{
+	gs_unref_hashtable GHashTable *hash = NULL;
+
+	g_ptr_array_unref (reader->array);
+	hash = g_steal_pointer (&reader->hash);
+	nm_g_slice_free (reader);
+	if (!free_hash)
+		return g_steal_pointer (&hash);
+	return NULL;
+}
+
+static NMConnection *
+reader_add_connection (Reader *reader, const char *name, NMConnection *connection_take)
+{
+	char *name_dup;
+
+	name_dup = g_strdup (name);
+	if (g_hash_table_insert (reader->hash, name_dup, connection_take))
+		g_ptr_array_add (reader->array, name_dup);
+
+	return connection_take;
 }
 
+/* Returns a new connection owned by the reader */
 static NMConnection *
-add_conn (GHashTable *connections,
-          const char *basename,
-          const char *id,
-          const char *ifname,
-          const char *type_name,
-          NMConnectionMultiConnect multi_connect)
+reader_create_connection (Reader *reader,
+                          const char *basename,
+                          const char *id,
+                          const char *ifname,
+                          const char *type_name,
+                          NMConnectionMultiConnect multi_connect)
 {
 	NMConnection *connection;
 	NMSetting *setting;
 
-	connection = nm_simple_connection_new ();
-	g_hash_table_insert (connections, g_strdup (basename), connection);
+	connection = reader_add_connection (reader,
+	                                    basename,
+	                                    nm_simple_connection_new ());
 
 	/* Start off assuming dynamic IP configurations. */
 
@@ -76,44 +110,74 @@ add_conn (GHashTable *connections,
 }
 
 static NMConnection *
-get_conn (GHashTable *connections, const char *ifname, const char *type_name)
+reader_get_default_connection (Reader *reader)
 {
-	NMConnection *connection;
+	NMConnection *con;
+
+	if (!reader->default_connection) {
+		con = reader_create_connection (reader,
+		                                "default_connection",
+		                                "Wired Connection",
+		                                NULL,
+		                                NM_SETTING_WIRED_SETTING_NAME,
+		                                NM_CONNECTION_MULTI_CONNECT_MULTIPLE);
+		reader->default_connection = con;
+	}
+	return reader->default_connection;
+}
+
+static NMConnection *
+reader_get_connection (Reader *reader,
+                       const char *ifname,
+                       const char *type_name,
+                       gboolean create_if_missing)
+{
+	NMConnection *connection = NULL;
 	NMSetting *setting;
-	const char *basename;
-	NMConnectionMultiConnect multi_connect;
 
-	if (ifname) {
-		basename = ifname;
-		multi_connect = NM_CONNECTION_MULTI_CONNECT_SINGLE;
-	} else {
-		/* This is essentially for the "ip=dhcp" scenario. */
-		basename = "default_connection";
-		multi_connect = NM_CONNECTION_MULTI_CONNECT_MULTIPLE;
-	}
+	if (!ifname) {
+		NMConnection *candidate;
+		NMSettingConnection *s_con;
+		guint i;
 
-	connection = g_hash_table_lookup (connections, (gpointer) basename);
-	if (!connection && !ifname) {
 		/*
 		 * If ifname was not given, we'll match the connection by type.
 		 * If the type was not given either, then we're happy with any connection but slaves.
 		 * This is so that things like "bond=bond0:eth1,eth2 nameserver=1.3.3.7 end up
 		 * slapping the nameserver to the most reasonable connection (bond0).
 		 */
-		connection = g_hash_table_find (connections,
-		                                _connection_matches_type,
-		                                (gpointer) type_name);
-	}
+		for (i = 0; i < reader->array->len; i++) {
+			candidate = g_hash_table_lookup (reader->hash, reader->array->pdata[i]);
+			s_con = nm_connection_get_setting_connection (candidate);
+
+			if (   type_name == NULL
+			    && nm_setting_connection_get_master (s_con) == NULL) {
+				connection = candidate;
+				break;
+			}
+
+			if (   type_name != NULL
+			    && nm_streq (nm_setting_connection_get_connection_type (s_con), type_name)) {
+				connection = candidate;
+				break;
+			}
+		}
+	} else
+		connection = g_hash_table_lookup (reader->hash, (gpointer) ifname);
 
 	if (!connection) {
+		if (!create_if_missing)
+			return NULL;
+
 		if (!type_name)
 			type_name = NM_SETTING_WIRED_SETTING_NAME;
 
-		connection = add_conn (connections, basename,
-		                       ifname ?: "Wired Connection",
-		                       ifname, type_name, multi_connect);
+		connection = reader_create_connection (reader, ifname,
+		                                       ifname ?: "Wired Connection",
+		                                       ifname, type_name,
+		                                       NM_CONNECTION_MULTI_CONNECT_SINGLE);
 	}
-	setting = (NMSetting *)nm_connection_get_setting_connection (connection);
+	setting = (NMSetting *) nm_connection_get_setting_connection (connection);
 
 	if (type_name) {
 		g_object_set (setting, NM_SETTING_CONNECTION_TYPE, type_name, NULL);
@@ -195,22 +259,27 @@ _base_setting_set (NMConnection *connection, const char *property, const char *v
 }
 
 static void
-read_all_connections_from_fw (GHashTable *connections, const char *sysfs_dir)
+reader_read_all_connections_from_fw (Reader *reader, const char *sysfs_dir)
 {
 	gs_unref_hashtable GHashTable *ibft = NULL;
-	NMConnection *connection;
-	GHashTableIter iter;
+	NMConnection *dt_connection;
 	const char *mac;
 	GHashTable *nic;
 	const char *index;
 	GError *error = NULL;
+	guint i, length;
+	gs_free const char **keys = NULL;
 
 	ibft = nmi_ibft_read (sysfs_dir);
+	keys = nm_utils_strdict_get_keys (ibft, TRUE, &length);
 
-	g_hash_table_iter_init (&iter, ibft);
-	while (g_hash_table_iter_next (&iter, (gpointer *) &mac, (gpointer *) &nic)) {
-		connection = nm_simple_connection_new ();
+	for (i = 0; i < length; i++) {
+		gs_unref_object NMConnection *connection = NULL;
+		gs_free char *name = NULL;
 
+		mac = keys[i];
+		nic = g_hash_table_lookup (ibft, mac);
+		connection = nm_simple_connection_new ();
 		index = g_hash_table_lookup (nic, "index");
 		if (!index) {
 			_LOGW (LOGD_CORE, "Ignoring an iBFT entry without an index");
@@ -220,23 +289,20 @@ read_all_connections_from_fw (GHashTable *connections, const char *sysfs_dir)
 		if (!nmi_ibft_update_connection_from_nic (connection, nic, &error)) {
 			_LOGW (LOGD_CORE, "Unable to merge iBFT configuration: %s", error->message);
 			g_error_free (error);
+			continue;
 		}
 
-		g_hash_table_insert (connections,
-		                     g_strdup_printf ("ibft%s", index),
-		                     connection);
+		name = g_strdup_printf ("ibft%s", index);
+		reader_add_connection (reader, name, g_steal_pointer (&connection));
 	}
 
-	connection = nmi_dt_reader_parse (sysfs_dir);
-	if (connection) {
-		g_hash_table_insert (connections,
-		                     g_strdup ("ofw"),
-		                     connection);
-	}
+	dt_connection = nmi_dt_reader_parse (sysfs_dir);
+	if (dt_connection)
+		reader_add_connection (reader, "ofw", dt_connection);
 }
 
 static void
-parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
+reader_parse_ip (Reader *reader, const char *sysfs_dir, char *argument)
 {
 	NMConnection *connection;
 	NMSettingIPConfig *s_ip4 = NULL, *s_ip6 = NULL;
@@ -299,12 +365,16 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
 
 	if (ifname == NULL && (   g_strcmp0 (kind, "fw") == 0
 	                       || g_strcmp0 (kind, "ibft") == 0)) {
-		read_all_connections_from_fw (connections, sysfs_dir);
+		reader_read_all_connections_from_fw (reader, sysfs_dir);
 		return;
 	}
 
 	/* Parsing done, construct the NMConnection. */
-	connection = get_conn (connections, ifname, NULL);
+	if (ifname)
+		connection = reader_get_connection (reader, ifname, NULL, TRUE);
+	else
+		connection = reader_get_default_connection (reader);
+
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 
@@ -390,7 +460,7 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
 		              NULL);
 		if (nm_setting_ip_config_get_num_addresses (s_ip6) == 0) {
 			g_object_set (s_ip6,
-			              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
+			              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
 			              NULL);
 		}
 	} else if (g_strcmp0 (kind, "dhcp6") == 0) {
@@ -445,7 +515,7 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
 	if (gateway_ip && *gateway_ip) {
 		int addr_family = guess_ip_address_family (gateway_ip);
 
-		if (nm_utils_ipaddr_valid (addr_family, gateway_ip)) {
+		if (nm_utils_ipaddr_is_valid (addr_family, gateway_ip)) {
 			switch (addr_family) {
 			case AF_INET:
 				g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway_ip, NULL);
@@ -470,7 +540,7 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
 	for (i = 0; i < 2; i++) {
 		if (dns_addr_family[i] == AF_UNSPEC)
 			break;
-		if (nm_utils_ipaddr_valid (dns_addr_family[i], dns[i])) {
+		if (nm_utils_ipaddr_is_valid (dns_addr_family[i], dns[i])) {
 			switch (dns_addr_family[i]) {
 			case AF_INET:
 				nm_setting_ip_config_add_dns (s_ip4, dns[i]);
@@ -495,10 +565,10 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
 }
 
 static void
-parse_master (GHashTable *connections,
-              char *argument,
-              const char *type_name,
-              const char *default_name)
+reader_parse_master (Reader *reader,
+                     char *argument,
+                     const char *type_name,
+                     const char *default_name)
 {
 	NMConnection *connection;
 	NMSettingConnection *s_con;
@@ -517,7 +587,7 @@ parse_master (GHashTable *connections,
 		master = master_to_free = g_strdup_printf ("%s0", default_name ?: type_name);
 	slaves = get_word (&argument, ':');
 
-	connection = get_conn (connections, master, type_name);
+	connection = reader_get_connection (reader, master, type_name, TRUE);
 	s_con = nm_connection_get_setting_connection (connection);
 	master = nm_setting_connection_get_uuid (s_con);
 
@@ -539,7 +609,7 @@ parse_master (GHashTable *connections,
 		if (slave == NULL)
 			slave = "eth0";
 
-		connection = get_conn (connections, slave, NULL);
+		connection = reader_get_connection (reader, slave, NULL, TRUE);
 		s_con = nm_connection_get_setting_connection (connection);
 		g_object_set (s_con,
 		              NM_SETTING_CONNECTION_SLAVE_TYPE, type_name,
@@ -554,69 +624,81 @@ parse_master (GHashTable *connections,
 }
 
 static void
-parse_rd_route (GHashTable *connections, char *argument)
+reader_add_routes (Reader *reader, GPtrArray *array)
 {
-	NMConnection *connection;
-	const char *net;
-	const char *gateway;
-	const char *interface;
-	int family = AF_UNSPEC;
-	NMIPAddr net_addr = { };
-	NMIPAddr gateway_addr = { };
-	int net_prefix = -1;
-	NMIPRoute *route;
-	NMSettingIPConfig *s_ip;
-	GError *error = NULL;
-
-	net = get_word (&argument, ':');
-	gateway = get_word (&argument, ':');
-	interface = get_word (&argument, ':');
-
-	connection = get_conn (connections, interface, NULL);
+	guint i;
+
+	for (i = 0; i < array->len; i++) {
+		NMConnection *connection = NULL;
+		const char *net;
+		const char *gateway;
+		const char *interface;
+		int family = AF_UNSPEC;
+		NMIPAddr net_addr = { };
+		NMIPAddr gateway_addr = { };
+		int net_prefix = -1;
+		NMIPRoute *route;
+		NMSettingIPConfig *s_ip;
+		char *argument;
+		gs_free_error GError *error = NULL;
+
+		argument = array->pdata[i];
+		net = get_word (&argument, ':');
+		gateway = get_word (&argument, ':');
+		interface = get_word (&argument, ':');
+
+		if (interface)
+			connection = reader_get_connection (reader, interface, NULL, TRUE);
+		if (!connection)
+			connection = reader->bootdev_connection;
+		if (!connection)
+			connection = reader_get_connection (reader, interface, NULL, FALSE);
+		if (!connection)
+			connection = reader_get_default_connection (reader);
+
+		if (net && *net) {
+			if (!nm_utils_parse_inaddr_prefix_bin (family, net, &family, &net_addr, &net_prefix)) {
+				_LOGW (LOGD_CORE, "Unrecognized address: %s", net);
+				continue;
+			}
+		}
 
-	if (net && *net) {
-		if (!nm_utils_parse_inaddr_prefix_bin (family, net, &family, &net_addr, &net_prefix)) {
-			_LOGW (LOGD_CORE, "Unrecognized address: %s", net);
-			return;
+		if (gateway && *gateway) {
+			if (!nm_utils_parse_inaddr_bin (family, gateway, &family, &gateway_addr)) {
+				_LOGW (LOGD_CORE, "Unrecognized address: %s", gateway);
+				continue;
+			}
 		}
-	}
 
-	if (gateway && *gateway) {
-		if (!nm_utils_parse_inaddr_bin (family, gateway, &family, &gateway_addr)) {
-			_LOGW (LOGD_CORE, "Unrecognized address: %s", gateway);
-			return;
+		switch (family) {
+		case AF_INET:
+			s_ip = nm_connection_get_setting_ip4_config (connection);
+			if (net_prefix == -1)
+				net_prefix = 32;
+			break;
+		case AF_INET6:
+			s_ip = nm_connection_get_setting_ip6_config (connection);
+			if (net_prefix == -1)
+				net_prefix = 128;
+			break;
+		default:
+			_LOGW (LOGD_CORE, "Unknown address family: %s", net);
+			continue;
 		}
-	}
 
-	switch (family) {
-	case AF_INET:
-		s_ip = nm_connection_get_setting_ip4_config (connection);
-		if (net_prefix == -1)
-			net_prefix = 32;
-		break;
-	case AF_INET6:
-		s_ip = nm_connection_get_setting_ip6_config (connection);
-		if (net_prefix == -1)
-			net_prefix = 128;
-		break;
-	default:
-		_LOGW (LOGD_CORE, "Unknown address family: %s", net);
-		return;
-	}
+		route = nm_ip_route_new_binary (family, &net_addr.addr_ptr, net_prefix, &gateway_addr.addr_ptr, -1, &error);
+		if (!route) {
+			g_warning ("Invalid route '%s via %s': %s\n", net, gateway, error->message);
+			continue;
+		}
 
-	route = nm_ip_route_new_binary (family, &net_addr.addr_ptr, net_prefix, &gateway_addr.addr_ptr, -1, &error);
-	if (!route) {
-		g_warning ("Invalid route '%s via %s': %s\n", net, gateway, error->message);
-		g_clear_error (&error);
-		return;
+		nm_setting_ip_config_add_route (s_ip, route);
+		nm_ip_route_unref (route);
 	}
-
-	nm_setting_ip_config_add_route (s_ip, route);
-	nm_ip_route_unref (route);
 }
 
 static void
-parse_vlan (GHashTable *connections, char *argument)
+reader_parse_vlan (Reader *reader, char *argument)
 {
 	NMConnection *connection;
 	NMSettingVlan *s_vlan;
@@ -632,90 +714,20 @@ parse_vlan (GHashTable *connections, char *argument)
 			break;
 	}
 
-	connection = get_conn (connections, vlan, NM_SETTING_VLAN_SETTING_NAME);
+	connection = reader_get_connection (reader, vlan, NM_SETTING_VLAN_SETTING_NAME, TRUE);
 
 	s_vlan = nm_connection_get_setting_vlan (connection);
 	g_object_set (s_vlan,
 	              NM_SETTING_VLAN_PARENT, phy,
-	              NM_SETTING_VLAN_ID, g_ascii_strtoull (vlanid, NULL, 10),
-	              NULL);
-
-	if (argument && *argument)
-		_LOGW (LOGD_CORE, "Ignoring extra: '%s'.", argument);
-}
-
-static void
-parse_bootdev (GHashTable *connections, char *argument)
-{
-	NMConnection *connection;
-	NMSettingConnection *s_con;
-
-	connection = get_conn (connections, NULL, NULL);
-
-	if (   nm_connection_get_interface_name (connection)
-	    && strcmp (nm_connection_get_interface_name (connection), argument) != 0) {
-		/* If the default connection already has an interface name,
-		 * we should not overwrite it. Create a new one instead. */
-		connection = get_conn (connections, argument, NULL);
-	}
-
-	s_con = nm_connection_get_setting_connection (connection);
-	g_object_set (s_con,
-	              NM_SETTING_CONNECTION_INTERFACE_NAME, argument,
+	              NM_SETTING_VLAN_ID, (guint) _nm_utils_ascii_str_to_int64 (vlanid, 10, 0, G_MAXUINT, G_MAXUINT),
 	              NULL);
-}
-
-static void
-parse_nameserver (GHashTable *connections, char *argument)
-{
-	NMConnection *connection;
-	NMSettingIPConfig *s_ip = NULL;
-	char *dns;
-
-	connection = get_conn (connections, NULL, NULL);
-
-	dns = get_word (&argument, '\0');
-
-	switch (guess_ip_address_family (dns)) {
-	case AF_INET:
-		s_ip = nm_connection_get_setting_ip4_config (connection);
-		break;
-	case AF_INET6:
-		s_ip = nm_connection_get_setting_ip6_config (connection);
-		break;
-	default:
-		_LOGW (LOGD_CORE, "Unknown address family: %s", dns);
-		break;
-	}
-
-	nm_setting_ip_config_add_dns (s_ip, dns);
 
 	if (argument && *argument)
 		_LOGW (LOGD_CORE, "Ignoring extra: '%s'.", argument);
 }
 
 static void
-parse_rd_peerdns (GHashTable *connections, char *argument)
-{
-	gboolean auto_dns = !_nm_utils_ascii_str_to_bool (argument, TRUE);
-	NMConnection *connection;
-	NMSettingIPConfig *s_ip = NULL;
-
-	connection = get_conn (connections, NULL, NULL);
-
-	s_ip = nm_connection_get_setting_ip4_config (connection);
-	g_object_set (s_ip,
-	              NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, auto_dns,
-	              NULL);
-
-	s_ip = nm_connection_get_setting_ip6_config (connection);
-	g_object_set (s_ip,
-	              NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, auto_dns,
-	              NULL);
-}
-
-static void
-parse_rd_znet (GHashTable *connections, char *argument, gboolean net_ifnames)
+reader_parse_rd_znet (Reader *reader, char *argument, gboolean net_ifnames)
 {
 	const char *nettype;
 	const char *subchannels[4] = { 0, 0, 0, 0 };
@@ -765,7 +777,7 @@ parse_rd_znet (GHashTable *connections, char *argument, gboolean net_ifnames)
 		ifname = g_strdup_printf ("%s%d", prefix, index);
 	}
 
-	connection = get_conn (connections, ifname, NM_SETTING_WIRED_SETTING_NAME);
+	connection = reader_get_connection (reader, ifname, NM_SETTING_WIRED_SETTING_NAME, TRUE);
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_object_set (s_wired,
 	              NM_SETTING_WIRED_S390_NETTYPE, nettype,
@@ -795,18 +807,89 @@ _normalize_conn (gpointer key, gpointer value, gpointer user_data)
 	nm_connection_normalize (connection, NULL, NULL, NULL);
 }
 
+static void
+reader_set_ignore_auto_dns (Reader *reader)
+{
+	GHashTableIter iter;
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip = NULL;
+
+	g_hash_table_iter_init (&iter, reader->hash);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) {
+		s_ip = nm_connection_get_setting_ip4_config (connection);
+		g_object_set (s_ip,
+		              NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, TRUE,
+		              NULL);
+
+		s_ip = nm_connection_get_setting_ip6_config (connection);
+		g_object_set (s_ip,
+		              NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, TRUE,
+		              NULL);
+	}
+}
+
+static void
+reader_add_nameservers (Reader *reader, GPtrArray *nameservers)
+{
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip;
+	GHashTableIter iter;
+	int addr_family;
+	const char *ns;
+	guint i;
+
+	for (i = 0; i < nameservers->len; i++) {
+		ns = nameservers->pdata[i];
+		addr_family = guess_ip_address_family (ns);
+		if (addr_family == AF_UNSPEC) {
+			_LOGW (LOGD_CORE, "Unknown address family: %s", ns);
+			continue;
+		}
+
+		g_hash_table_iter_init (&iter, reader->hash);
+		while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) {
+			switch (addr_family) {
+			case AF_INET:
+				s_ip = nm_connection_get_setting_ip4_config (connection);
+				if (!NM_IN_STRSET (nm_setting_ip_config_get_method (s_ip),
+				                   NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+				                   NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
+					continue;
+				break;
+			case AF_INET6:
+				s_ip = nm_connection_get_setting_ip6_config (connection);
+				if (!NM_IN_STRSET (nm_setting_ip_config_get_method (s_ip),
+				                   NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+				                   NM_SETTING_IP6_CONFIG_METHOD_DHCP,
+				                   NM_SETTING_IP6_CONFIG_METHOD_MANUAL))
+					continue;
+				break;
+			default:
+				nm_assert_not_reached ();
+				continue;
+			}
+
+			nm_setting_ip_config_add_dns (s_ip, ns);
+		}
+	}
+}
+
 GHashTable *
 nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv)
 {
-	GHashTable *connections;
+	Reader *reader;
 	const char *tag;
 	gboolean ignore_bootif = FALSE;
 	gboolean neednet = FALSE;
 	gs_free char *bootif_val = NULL;
+	gs_free char *bootdev = NULL;
 	gboolean net_ifnames = TRUE;
+	gs_unref_ptrarray GPtrArray *nameservers = NULL;
+	gs_unref_ptrarray GPtrArray *routes = NULL;
+	gboolean ignore_auto_dns = FALSE;
 	int i;
 
-	connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
+	reader = reader_new ();
 
 	for (i = 0; argv[i]; i++) {
 		if (strcmp (argv[i], "net.ifnames=0") == 0)
@@ -818,38 +901,49 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv)
 	for (i = 0; argv[i]; i++) {
 		gs_free char *argument_clone = NULL;
 		char *argument;
+		char *word;
 
 		argument_clone = g_strdup (argv[i]);
 		argument = argument_clone;
 
 		tag = get_word (&argument, '=');
 		if (strcmp (tag, "ip") == 0)
-			parse_ip (connections, sysfs_dir, argument);
-		else if (strcmp (tag, "rd.route") == 0)
-			parse_rd_route (connections, argument);
-		else if (strcmp (tag, "bridge") == 0)
-			parse_master (connections, argument, NM_SETTING_BRIDGE_SETTING_NAME, "br");
+			reader_parse_ip (reader, sysfs_dir, argument);
+		else if (strcmp (tag, "rd.route") == 0) {
+			if (!routes)
+				routes = g_ptr_array_new_with_free_func (g_free);
+			g_ptr_array_add (routes, g_strdup (argument));
+		} else if (strcmp (tag, "bridge") == 0)
+			reader_parse_master (reader, argument, NM_SETTING_BRIDGE_SETTING_NAME, "br");
 		else if (strcmp (tag, "bond") == 0)
-			parse_master (connections, argument, NM_SETTING_BOND_SETTING_NAME, NULL);
+			reader_parse_master (reader, argument, NM_SETTING_BOND_SETTING_NAME, NULL);
 		else if (strcmp (tag, "team") == 0)
-			parse_master (connections, argument, NM_SETTING_TEAM_SETTING_NAME, NULL);
+			reader_parse_master (reader, argument, NM_SETTING_TEAM_SETTING_NAME, NULL);
 		else if (strcmp (tag, "vlan") == 0)
-			parse_vlan (connections, argument);
-		else if (strcmp (tag, "bootdev") == 0)
-			parse_bootdev (connections, argument);
-		else if (strcmp (tag, "nameserver") == 0)
-			parse_nameserver (connections, argument);
-		else if (strcmp (tag, "rd.peerdns") == 0)
-			parse_rd_peerdns (connections, argument);
+			reader_parse_vlan (reader, argument);
+		else if (strcmp (tag, "bootdev") == 0) {
+			g_free (bootdev);
+			bootdev = g_strdup (argument);
+		} else if (strcmp (tag, "nameserver") == 0) {
+			word = get_word (&argument, '\0');
+			if (word) {
+				if (!nameservers)
+					nameservers = g_ptr_array_new_with_free_func (g_free);
+				g_ptr_array_add (nameservers, g_strdup (word));
+			}
+			if (argument && *argument)
+				_LOGW (LOGD_CORE, "Ignoring extra: '%s'.", argument);
+		} else if (strcmp (tag, "rd.peerdns") == 0)
+			ignore_auto_dns = !_nm_utils_ascii_str_to_bool (argument, TRUE);
 		else if (strcmp (tag, "rd.iscsi.ibft") == 0 && _nm_utils_ascii_str_to_bool (argument, TRUE))
-			read_all_connections_from_fw (connections, sysfs_dir);
+			reader_read_all_connections_from_fw (reader, sysfs_dir);
 		else if (strcmp (tag, "rd.bootif") == 0)
 			ignore_bootif = !_nm_utils_ascii_str_to_bool (argument, TRUE);
 		else if (strcmp (tag, "rd.neednet") == 0)
 			neednet = _nm_utils_ascii_str_to_bool (argument, TRUE);
 		else if (strcmp (tag, "rd.znet") == 0)
-			parse_rd_znet (connections, argument, net_ifnames);
-		else if (strcasecmp (tag, "BOOTIF") == 0) {
+			reader_parse_rd_znet (reader, argument, net_ifnames);
+		else if (g_ascii_strcasecmp (tag, "BOOTIF") == 0) {
 			nm_clear_g_free (&bootif_val);
 			bootif_val = g_strdup (argument);
 		}
@@ -872,16 +966,22 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv)
 			bootif += 3;
 		}
 
-		connection = get_conn (connections, NULL, NM_SETTING_WIRED_SETTING_NAME);
+		connection = reader_get_connection (reader, NULL, NM_SETTING_WIRED_SETTING_NAME, FALSE);
+		if (!connection)
+			connection = reader_get_default_connection (reader);
+
 		s_wired = nm_connection_get_setting_wired (connection);
 
 		if (   nm_connection_get_interface_name (connection)
 		    || (   nm_setting_wired_get_mac_address (s_wired)
 		        && !nm_utils_hwaddr_matches (nm_setting_wired_get_mac_address (s_wired), -1,
 		                                     bootif, -1))) {
-			connection = add_conn (connections, "bootif_connection", "BOOTIF Connection",
-			                       NULL, NM_SETTING_WIRED_SETTING_NAME,
-			                       NM_CONNECTION_MULTI_CONNECT_SINGLE);
+			connection = reader_create_connection (reader,
+			                                       "bootif_connection",
+			                                       "BOOTIF Connection",
+			                                       NULL,
+			                                       NM_SETTING_WIRED_SETTING_NAME,
+			                                       NM_CONNECTION_MULTI_CONNECT_SINGLE);
 			s_wired = (NMSettingWired *) nm_setting_wired_new ();
 			nm_connection_add_setting (connection, (NMSetting *) s_wired);
 		}
@@ -890,12 +990,28 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv)
 		              NM_SETTING_WIRED_MAC_ADDRESS, bootif,
 		              NULL);
 	}
-	if (neednet && g_hash_table_size (connections) == 0) {
+
+	if (bootdev) {
+		NMConnection *connection;
+
+		connection = reader_get_connection (reader, bootdev, NULL, TRUE);
+		reader->bootdev_connection = connection;
+	}
+
+	if (neednet && g_hash_table_size (reader->hash) == 0) {
 		/* Make sure there's some connection. */
-		get_conn (connections, NULL, NM_SETTING_WIRED_SETTING_NAME);
+		reader_get_default_connection (reader);
 	}
 
-	g_hash_table_foreach (connections, _normalize_conn, NULL);
+	if (routes)
+		reader_add_routes (reader, routes);
+
+	if (nameservers)
+		reader_add_nameservers (reader, nameservers);
+
+	if (ignore_auto_dns)
+		reader_set_ignore_auto_dns (reader);
 
-	return connections;
+	g_hash_table_foreach (reader->hash, _normalize_conn, NULL);
+	return reader_destroy (reader, FALSE);
 }