git: aea7fa728b0f - main - ctld: Switch to the C++ bindings for libucl

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Mon, 04 Aug 2025 19:46:40 UTC
The branch main has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=aea7fa728b0f3f31f4e8e9f13bbf0d69edb4a0cd

commit aea7fa728b0f3f31f4e8e9f13bbf0d69edb4a0cd
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2025-08-04 19:38:06 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2025-08-04 19:38:06 +0000

    ctld: Switch to the C++ bindings for libucl
    
    In particular, this permits using ranged-for loops to iterate over
    keys in an object which is more readable.
    
    Sponsored by:   Chelsio Communications
    Pull Request:   https://github.com/freebsd/freebsd-src/pull/1794
---
 usr.sbin/ctld/Makefile    |   4 +-
 usr.sbin/ctld/uclparse.cc | 709 ++++++++++++++++++++--------------------------
 2 files changed, 317 insertions(+), 396 deletions(-)

diff --git a/usr.sbin/ctld/Makefile b/usr.sbin/ctld/Makefile
index 67f89dca2757..26cc03c036cb 100644
--- a/usr.sbin/ctld/Makefile
+++ b/usr.sbin/ctld/Makefile
@@ -18,7 +18,7 @@ NO_WCAST_ALIGN=
 CXXWARNFLAGS.gcc= -Wno-shadow
 MAN=		ctld.8 ctl.conf.5
 
-LIBADD=		bsdxml iscsiutil md sbuf util ucl m nv
+LIBADD=		bsdxml iscsiutil md sbuf util ucl m nv util++
 
 YFLAGS+=	-v
 CLEANFILES=	y.tab.c y.tab.h y.output
@@ -30,3 +30,5 @@ CFLAGS+=	-DWANT_ISCSI
 .endif
 
 .include <bsd.prog.mk>
+
+CXXWARNFLAGS.uclparse.cc= -Wno-shadow -Wno-cast-qual
diff --git a/usr.sbin/ctld/uclparse.cc b/usr.sbin/ctld/uclparse.cc
index 4c7bbb43d75d..722b045effb6 100644
--- a/usr.sbin/ctld/uclparse.cc
+++ b/usr.sbin/ctld/uclparse.cc
@@ -37,299 +37,280 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
-#include <ucl.h>
+#include <ucl++.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 
+#include <libutil++.hh>
 #include <memory>
 
 #include "conf.h"
 #include "ctld.hh"
 
-static bool uclparse_toplevel(const ucl_object_t *);
-static bool uclparse_chap(const char *, const ucl_object_t *);
-static bool uclparse_chap_mutual(const char *, const ucl_object_t *);
-static bool uclparse_lun(const char *, const ucl_object_t *);
-static bool uclparse_lun_entries(const char *, const ucl_object_t *);
-static bool uclparse_auth_group(const char *, const ucl_object_t *);
-static bool uclparse_portal_group(const char *, const ucl_object_t *);
-static bool uclparse_target(const char *, const ucl_object_t *);
-static bool uclparse_target_portal_group(const char *, const ucl_object_t *);
-static bool uclparse_target_lun(const char *, const ucl_object_t *);
+static bool uclparse_toplevel(const ucl::Ucl &);
+static bool uclparse_chap(const char *, const ucl::Ucl &);
+static bool uclparse_chap_mutual(const char *, const ucl::Ucl &);
+static bool uclparse_lun(const char *, const ucl::Ucl &);
+static bool uclparse_lun_entries(const char *, const ucl::Ucl &);
+static bool uclparse_auth_group(const char *, const ucl::Ucl &);
+static bool uclparse_portal_group(const char *, const ucl::Ucl &);
+static bool uclparse_target(const char *, const ucl::Ucl &);
+static bool uclparse_target_portal_group(const char *, const ucl::Ucl &);
+static bool uclparse_target_lun(const char *, const ucl::Ucl &);
 
 static bool
-uclparse_chap(const char *ag_name, const ucl_object_t *obj)
+uclparse_chap(const char *ag_name, const ucl::Ucl &obj)
 {
-	const ucl_object_t *user, *secret;
-
-	user = ucl_object_find_key(obj, "user");
-	if (!user || user->type != UCL_STRING) {
+	auto user = obj["user"];
+	if (!user || user.type() != UCL_STRING) {
 		log_warnx("chap section in auth-group \"%s\" is missing "
 		    "\"user\" string key", ag_name);
 		return (false);
 	}
 
-	secret = ucl_object_find_key(obj, "secret");
-	if (!secret || secret->type != UCL_STRING) {
+	auto secret = obj["secret"];
+	if (!secret || secret.type() != UCL_STRING) {
 		log_warnx("chap section in auth-group \"%s\" is missing "
 		    "\"secret\" string key", ag_name);
 		return (false);
 	}
 
 	return (auth_group_add_chap(
-	    ucl_object_tostring(user),
-	    ucl_object_tostring(secret)));
+	    user.string_value().c_str(),
+	    secret.string_value().c_str()));
 }
 
 static bool
-uclparse_chap_mutual(const char *ag_name, const ucl_object_t *obj)
+uclparse_chap_mutual(const char *ag_name, const ucl::Ucl &obj)
 {
-	const ucl_object_t *user, *secret, *mutual_user;
-	const ucl_object_t *mutual_secret;
-
-	user = ucl_object_find_key(obj, "user");
-	if (!user || user->type != UCL_STRING) {
+	auto user = obj["user"];
+	if (!user || user.type() != UCL_STRING) {
 		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
 		    "\"user\" string key", ag_name);
 		return (false);
 	}
 
-	secret = ucl_object_find_key(obj, "secret");
-	if (!secret || secret->type != UCL_STRING) {
+	auto secret = obj["secret"];
+	if (!secret || secret.type() != UCL_STRING) {
 		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
 		    "\"secret\" string key", ag_name);
 		return (false);
 	}
 
-	mutual_user = ucl_object_find_key(obj, "mutual-user");
-	if (!mutual_user || mutual_user->type != UCL_STRING) {
+	auto mutual_user = obj["mutual-user"];
+	if (!mutual_user || mutual_user.type() != UCL_STRING) {
 		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
 		    "\"mutual-user\" string key", ag_name);
 		return (false);
 	}
 
-	mutual_secret = ucl_object_find_key(obj, "mutual-secret");
-	if (!mutual_secret || mutual_secret->type != UCL_STRING) {
+	auto mutual_secret = obj["mutual-secret"];
+	if (!mutual_secret || mutual_secret.type() != UCL_STRING) {
 		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
 		    "\"mutual-secret\" string key", ag_name);
 		return (false);
 	}
 
 	return (auth_group_add_chap_mutual(
-	    ucl_object_tostring(user),
-	    ucl_object_tostring(secret),
-	    ucl_object_tostring(mutual_user),
-	    ucl_object_tostring(mutual_secret)));
+	    user.string_value().c_str(),
+	    secret.string_value().c_str(),
+	    mutual_user.string_value().c_str(),
+	    mutual_secret.string_value().c_str()));
 }
 
 static bool
-uclparse_target_chap(const char *t_name, const ucl_object_t *obj)
+uclparse_target_chap(const char *t_name, const ucl::Ucl &obj)
 {
-	const ucl_object_t *user, *secret;
-
-	user = ucl_object_find_key(obj, "user");
-	if (!user || user->type != UCL_STRING) {
+	auto user = obj["user"];
+	if (!user || user.type() != UCL_STRING) {
 		log_warnx("chap section in target \"%s\" is missing "
 		    "\"user\" string key", t_name);
 		return (false);
 	}
 
-	secret = ucl_object_find_key(obj, "secret");
-	if (!secret || secret->type != UCL_STRING) {
+	auto secret = obj["secret"];
+	if (!secret || secret.type() != UCL_STRING) {
 		log_warnx("chap section in target \"%s\" is missing "
 		    "\"secret\" string key", t_name);
 		return (false);
 	}
 
 	return (target_add_chap(
-	    ucl_object_tostring(user),
-	    ucl_object_tostring(secret)));
+	    user.string_value().c_str(),
+	    secret.string_value().c_str()));
 }
 
 static bool
-uclparse_target_chap_mutual(const char *t_name, const ucl_object_t *obj)
+uclparse_target_chap_mutual(const char *t_name, const ucl::Ucl &obj)
 {
-	const ucl_object_t *user, *secret, *mutual_user;
-	const ucl_object_t *mutual_secret;
-
-	user = ucl_object_find_key(obj, "user");
-	if (!user || user->type != UCL_STRING) {
+	auto user = obj["user"];
+	if (!user || user.type() != UCL_STRING) {
 		log_warnx("chap-mutual section in target \"%s\" is missing "
 		    "\"user\" string key", t_name);
 		return (false);
 	}
 
-	secret = ucl_object_find_key(obj, "secret");
-	if (!secret || secret->type != UCL_STRING) {
+	auto secret = obj["secret"];
+	if (!secret || secret.type() != UCL_STRING) {
 		log_warnx("chap-mutual section in target \"%s\" is missing "
 		    "\"secret\" string key", t_name);
 		return (false);
 	}
 
-	mutual_user = ucl_object_find_key(obj, "mutual-user");
-	if (!mutual_user || mutual_user->type != UCL_STRING) {
+	auto mutual_user = obj["mutual-user"];
+	if (!mutual_user || mutual_user.type() != UCL_STRING) {
 		log_warnx("chap-mutual section in target \"%s\" is missing "
 		    "\"mutual-user\" string key", t_name);
 		return (false);
 	}
 
-	mutual_secret = ucl_object_find_key(obj, "mutual-secret");
-	if (!mutual_secret || mutual_secret->type != UCL_STRING) {
+	auto mutual_secret = obj["mutual-secret"];
+	if (!mutual_secret || mutual_secret.type() != UCL_STRING) {
 		log_warnx("chap-mutual section in target \"%s\" is missing "
 		    "\"mutual-secret\" string key", t_name);
 		return (false);
 	}
 
 	return (target_add_chap_mutual(
-	    ucl_object_tostring(user),
-	    ucl_object_tostring(secret),
-	    ucl_object_tostring(mutual_user),
-	    ucl_object_tostring(mutual_secret)));
+	    user.string_value().c_str(),
+	    secret.string_value().c_str(),
+	    mutual_user.string_value().c_str(),
+	    mutual_secret.string_value().c_str()));
 }
 
 static bool
-uclparse_target_portal_group(const char *t_name, const ucl_object_t *obj)
+uclparse_target_portal_group(const char *t_name, const ucl::Ucl &obj)
 {
-	const ucl_object_t *portal_group, *auth_group;
-	const char *ag_name;
-
 	/*
 	 * If the value is a single string, assume it is a
 	 * portal-group name.
 	 */
-	if (obj->type == UCL_STRING)
-		return (target_add_portal_group(ucl_object_tostring(obj),
+	if (obj.type() == UCL_STRING)
+		return (target_add_portal_group(obj.string_value().c_str(),
 		    NULL));
 
-	if (obj->type != UCL_OBJECT) {
+	if (obj.type() != UCL_OBJECT) {
 		log_warnx("portal-group section in target \"%s\" must be "
 		    "an object or string", t_name);
 		return (false);
 	}
 
-	portal_group = ucl_object_find_key(obj, "name");
-	if (!portal_group || portal_group->type != UCL_STRING) {
+	auto portal_group = obj["name"];
+	if (!portal_group || portal_group.type() != UCL_STRING) {
 		log_warnx("portal-group section in target \"%s\" is missing "
 		    "\"name\" string key", t_name);
 		return (false);
 	}
 
-	auth_group = ucl_object_find_key(obj, "auth-group-name");
-	if (auth_group != NULL) {
-		if (auth_group->type != UCL_STRING) {
+	auto auth_group = obj["auth-group-name"];
+	if (auth_group) {
+		if (auth_group.type() != UCL_STRING) {
 			log_warnx("\"auth-group-name\" property in "
 			    "portal-group section for target \"%s\" is not "
 			    "a string", t_name);
 			return (false);
 		}
-		ag_name = ucl_object_tostring(auth_group);
-	} else
-		ag_name = NULL;
+		return (target_add_portal_group(
+		    portal_group.string_value().c_str(),
+		    auth_group.string_value().c_str()));
+	}
 
-	return (target_add_portal_group(ucl_object_tostring(portal_group),
-	    ag_name));
+	return (target_add_portal_group(portal_group.string_value().c_str(),
+	    NULL));
 }
 
 static bool
-uclparse_target_lun(const char *t_name, const ucl_object_t *obj)
+uclparse_target_lun(const char *t_name, const ucl::Ucl &obj)
 {
-	const ucl_object_t *num;
-	const ucl_object_t *name;
-	const char *key;
-	char *end, *lun_name;
+	char *end;
 	u_int id;
-	bool ok;
 
-	key = ucl_object_key(obj);
-	if (key != NULL) {
-		id = strtoul(key, &end, 0);
+	std::string key = obj.key();
+	if (!key.empty()) {
+		id = strtoul(key.c_str(), &end, 0);
 		if (*end != '\0') {
 			log_warnx("lun key \"%s\" in target \"%s\" is invalid",
-			    key, t_name);
+			    key.c_str(), t_name);
 			return (false);
 		}
 
-		if (obj->type == UCL_STRING)
-			return (target_add_lun(id, ucl_object_tostring(obj)));
+		if (obj.type() == UCL_STRING)
+			return (target_add_lun(id, obj.string_value().c_str()));
 	}
 
-	if (obj->type != UCL_OBJECT) {
+	if (obj.type() != UCL_OBJECT) {
 		log_warnx("lun section entries in target \"%s\" must be objects",
 		    t_name);
 		return (false);
 	}
 
-	if (key == NULL) {
-		num = ucl_object_find_key(obj, "number");
-		if (num == NULL || num->type != UCL_INT) {
+	if (key.empty()) {
+		auto num = obj["number"];
+		if (!num || num.type() != UCL_INT) {
 			log_warnx("lun section in target \"%s\" is missing "
 			    "\"number\" integer property", t_name);
 			return (false);
 		}
-		id = ucl_object_toint(num);
+		id = num.int_value();
 	}
 
-	name = ucl_object_find_key(obj, "name");
-	if (name == NULL) {
+	auto name = obj["name"];
+	if (!name) {
 		if (!target_start_lun(id))
 			return (false);
 
-		asprintf(&lun_name, "lun %u for target \"%s\"", id, t_name);
-		ok = uclparse_lun_entries(lun_name, obj);
-		free(lun_name);
-		return (ok);
+		std::string lun_name =
+		    freebsd::stringf("lun %u for target \"%s\"", id, t_name);
+		return (uclparse_lun_entries(lun_name.c_str(), obj));
 	}
 
-	if (name->type != UCL_STRING) {
+	if (name.type() != UCL_STRING) {
 		log_warnx("\"name\" property for lun %u for target "
 		    "\"%s\" is not a string", id, t_name);
 		return (false);
 	}
 
-	return (target_add_lun(id, ucl_object_tostring(name)));
+	return (target_add_lun(id, name.string_value().c_str()));
 }
 
 static bool
-uclparse_toplevel(const ucl_object_t *top)
+uclparse_toplevel(const ucl::Ucl &top)
 {
-	ucl_object_iter_t it = NULL, iter = NULL;
-	const ucl_object_t *obj = NULL, *child = NULL;
-
 	/* Pass 1 - everything except targets */
-	while ((obj = ucl_iterate_object(top, &it, true))) {
-		const char *key = ucl_object_key(obj);
+	for (const auto &obj : top) {
+		std::string key = obj.key();
 
-		if (strcmp(key, "debug") == 0) {
-			if (obj->type == UCL_INT)
-				conf_set_debug(ucl_object_toint(obj));
+		if (key == "debug") {
+			if (obj.type() == UCL_INT)
+				conf_set_debug(obj.int_value());
 			else {
 				log_warnx("\"debug\" property value is not integer");
 				return (false);
 			}
 		}
 
-		if (strcmp(key, "timeout") == 0) {
-			if (obj->type == UCL_INT)
-				conf_set_timeout(ucl_object_toint(obj));
+		if (key == "timeout") {
+			if (obj.type() == UCL_INT)
+				conf_set_timeout(obj.int_value());
 			else {
 				log_warnx("\"timeout\" property value is not integer");
 				return (false);
 			}
 		}
 
-		if (strcmp(key, "maxproc") == 0) {
-			if (obj->type == UCL_INT)
-				conf_set_maxproc(ucl_object_toint(obj));
+		if (key == "maxproc") {
+			if (obj.type() == UCL_INT)
+				conf_set_maxproc(obj.int_value());
 			else {
 				log_warnx("\"maxproc\" property value is not integer");
 				return (false);
 			}
 		}
 
-		if (strcmp(key, "pidfile") == 0) {
-			if (obj->type == UCL_STRING) {
+		if (key == "pidfile") {
+			if (obj.type() == UCL_STRING) {
 				if (!conf_set_pidfile_path(
-				    ucl_object_tostring(obj)))
+				    obj.string_value().c_str()))
 					return (false);
 			} else {
 				log_warnx("\"pidfile\" property value is not string");
@@ -337,16 +318,14 @@ uclparse_toplevel(const ucl_object_t *top)
 			}
 		}
 
-		if (strcmp(key, "isns-server") == 0) {
-			if (obj->type == UCL_ARRAY) {
-				iter = NULL;
-				while ((child = ucl_iterate_object(obj, &iter,
-				    true))) {
-					if (child->type != UCL_STRING)
+		if (key == "isns-server") {
+			if (obj.type() == UCL_ARRAY) {
+				for (const auto &child : obj) {
+					if (child.type() != UCL_STRING)
 						return (false);
 
 					if (!isns_add_server(
-					    ucl_object_tostring(child)))
+					    child.string_value().c_str()))
 						return (false);
 				}
 			} else {
@@ -356,30 +335,29 @@ uclparse_toplevel(const ucl_object_t *top)
 			}
 		}
 
-		if (strcmp(key, "isns-period") == 0) {
-			if (obj->type == UCL_INT)
-				conf_set_isns_period(ucl_object_toint(obj));
+		if (key == "isns-period") {
+			if (obj.type() == UCL_INT)
+				conf_set_isns_period(obj.int_value());
 			else {
 				log_warnx("\"isns-period\" property value is not integer");
 				return (false);
 			}
 		}
 
-		if (strcmp(key, "isns-timeout") == 0) {
-			if (obj->type == UCL_INT)
-				conf_set_isns_timeout(ucl_object_toint(obj));
+		if (key == "isns-timeout") {
+			if (obj.type() == UCL_INT)
+				conf_set_isns_timeout(obj.int_value());
 			else {
 				log_warnx("\"isns-timeout\" property value is not integer");
 				return (false);
 			}
 		}
 
-		if (strcmp(key, "auth-group") == 0) {
-			if (obj->type == UCL_OBJECT) {
-				iter = NULL;
-				while ((child = ucl_iterate_object(obj, &iter, true))) {
+		if (key == "auth-group") {
+			if (obj.type() == UCL_OBJECT) {
+				for (const auto &child : obj) {
 					if (!uclparse_auth_group(
-					    ucl_object_key(child), child))
+					    child.key().c_str(), child))
 						return (false);
 				}
 			} else {
@@ -388,12 +366,11 @@ uclparse_toplevel(const ucl_object_t *top)
 			}
 		}
 
-		if (strcmp(key, "portal-group") == 0) {
-			if (obj->type == UCL_OBJECT) {
-				iter = NULL;
-				while ((child = ucl_iterate_object(obj, &iter, true))) {
+		if (key == "portal-group") {
+			if (obj.type() == UCL_OBJECT) {
+				for (const auto &child : obj) {
 					if (!uclparse_portal_group(
-					    ucl_object_key(child), child))
+					    child.key().c_str(), child))
 						return (false);
 				}
 			} else {
@@ -402,11 +379,10 @@ uclparse_toplevel(const ucl_object_t *top)
 			}
 		}
 
-		if (strcmp(key, "lun") == 0) {
-			if (obj->type == UCL_OBJECT) {
-				iter = NULL;
-				while ((child = ucl_iterate_object(obj, &iter, true))) {
-					if (!uclparse_lun(ucl_object_key(child),
+		if (key == "lun") {
+			if (obj.type() == UCL_OBJECT) {
+				for (const auto &child : obj) {
+					if (!uclparse_lun(child.key().c_str(),
 					    child))
 						return (false);
 				}
@@ -418,17 +394,14 @@ uclparse_toplevel(const ucl_object_t *top)
 	}
 
 	/* Pass 2 - targets */
-	it = NULL;
-	while ((obj = ucl_iterate_object(top, &it, true))) {
-		const char *key = ucl_object_key(obj);
-
-		if (strcmp(key, "target") == 0) {
-			if (obj->type == UCL_OBJECT) {
-				iter = NULL;
-				while ((child = ucl_iterate_object(obj, &iter,
-				    true))) {
+	for (const auto &obj : top) {
+		std::string key = obj.key();
+
+		if (key == "target") {
+			if (obj.type() == UCL_OBJECT) {
+				for (const auto &child : obj) {
 					if (!uclparse_target(
-					    ucl_object_key(child), child))
+					    child.key().c_str(), child))
 						return (false);
 				}
 			} else {
@@ -442,33 +415,25 @@ uclparse_toplevel(const ucl_object_t *top)
 }
 
 static bool
-uclparse_auth_group(const char *name, const ucl_object_t *top)
+uclparse_auth_group(const char *name, const ucl::Ucl &top)
 {
-	ucl_object_iter_t it = NULL, it2 = NULL;
-	const ucl_object_t *obj = NULL, *tmp = NULL;
-	const char *key;
-
 	if (!auth_group_start(name))
 		return (false);
 
-	while ((obj = ucl_iterate_object(top, &it, true))) {
-		key = ucl_object_key(obj);
+	for (const auto &obj : top) {
+		std::string key = obj.key();
 
-		if (strcmp(key, "auth-type") == 0) {
-			const char *value = ucl_object_tostring(obj);
-
-			if (!auth_group_set_type(value))
+		if (key == "auth-type") {
+			if (!auth_group_set_type(obj.string_value().c_str()))
 				goto fail;
 		}
 
-		if (strcmp(key, "chap") == 0) {
-			if (obj->type == UCL_OBJECT) {
+		if (key == "chap") {
+			if (obj.type() == UCL_OBJECT) {
 				if (!uclparse_chap(name, obj))
 					goto fail;
-			} else if (obj->type == UCL_ARRAY) {
-				it2 = NULL;
-				while ((tmp = ucl_iterate_object(obj, &it2,
-				    true))) {
+			} else if (obj.type() == UCL_ARRAY) {
+				for (const auto &tmp : obj) {
 					if (!uclparse_chap(name, tmp))
 						goto fail;
 				}
@@ -480,14 +445,12 @@ uclparse_auth_group(const char *name, const ucl_object_t *top)
 			}
 		}
 
-		if (strcmp(key, "chap-mutual") == 0) {
-			if (obj->type == UCL_OBJECT) {
+		if (key == "chap-mutual") {
+			if (obj.type() == UCL_OBJECT) {
 				if (!uclparse_chap_mutual(name, obj))
 					goto fail;
-			} else if (obj->type == UCL_ARRAY) {
-				it2 = NULL;
-				while ((tmp = ucl_iterate_object(obj, &it2,
-				    true))) {
+			} else if (obj.type() == UCL_ARRAY) {
+				for (const auto &tmp : obj) {
 					if (!uclparse_chap_mutual(name, tmp))
 						goto fail;
 				}
@@ -499,21 +462,15 @@ uclparse_auth_group(const char *name, const ucl_object_t *top)
 			}
 		}
 
-		if (strcmp(key, "initiator-name") == 0) {
-			if (obj->type == UCL_STRING) {
-				const char *value = ucl_object_tostring(obj);
-
-				if (!auth_group_add_initiator_name(value))
+		if (key == "initiator-name") {
+			if (obj.type() == UCL_STRING) {
+				if (!auth_group_add_initiator_name(
+				    obj.string_value().c_str()))
 					goto fail;
-			} else if (obj->type == UCL_ARRAY) {
-				it2 = NULL;
-				while ((tmp = ucl_iterate_object(obj, &it2,
-				    true))) {
-					const char *value =
-					    ucl_object_tostring(tmp);
-
+			} else if (obj.type() == UCL_ARRAY) {
+				for (const auto &tmp : obj) {
 					if (!auth_group_add_initiator_name(
-					    value))
+					    tmp.string_value().c_str()))
 						goto fail;
 				}
 			} else {
@@ -524,21 +481,15 @@ uclparse_auth_group(const char *name, const ucl_object_t *top)
 			}
 		}
 
-		if (strcmp(key, "initiator-portal") == 0) {
-			if (obj->type == UCL_STRING) {
-				const char *value = ucl_object_tostring(obj);
-
-				if (!auth_group_add_initiator_portal(value))
+		if (key == "initiator-portal") {
+			if (obj.type() == UCL_STRING) {
+				if (!auth_group_add_initiator_portal(
+				    obj.string_value().c_str()))
 					goto fail;
-			} else if (obj->type == UCL_ARRAY) {
-				it2 = NULL;
-				while ((tmp = ucl_iterate_object(obj, &it2,
-				    true))) {
-					const char *value =
-					    ucl_object_tostring(tmp);
-
+			} else if (obj.type() == UCL_ARRAY) {
+				for (const auto &tmp : obj) {
 					if (!auth_group_add_initiator_portal(
-					    value))
+					    tmp.string_value().c_str()))
 						goto fail;
 				}
 			} else {
@@ -559,62 +510,60 @@ fail:
 
 static bool
 uclparse_dscp(const char *group_type, const char *pg_name,
-    const ucl_object_t *obj)
+    const ucl::Ucl &obj)
 {
-	const char *key;
-
-	if ((obj->type != UCL_STRING) && (obj->type != UCL_INT)) {
+	if ((obj.type() != UCL_STRING) && (obj.type() != UCL_INT)) {
 		log_warnx("\"dscp\" property of %s group \"%s\" is not a "
 		    "string or integer", group_type, pg_name);
 		return (false);
 	}
-	if (obj->type == UCL_INT)
-		return (portal_group_set_dscp(ucl_object_toint(obj)));
+	if (obj.type() == UCL_INT)
+		return (portal_group_set_dscp(obj.int_value()));
 
-	key = ucl_object_tostring(obj);
-	if (strcmp(key, "be") == 0 || strcmp(key, "cs0") == 0)
+	std::string key = obj.key();
+	if (key == "be" || key == "cs0")
 		portal_group_set_dscp(IPTOS_DSCP_CS0 >> 2);
-	else if (strcmp(key, "ef") == 0)
+	else if (key == "ef")
 		portal_group_set_dscp(IPTOS_DSCP_EF >> 2);
-	else if (strcmp(key, "cs0") == 0)
+	else if (key == "cs0")
 		portal_group_set_dscp(IPTOS_DSCP_CS0 >> 2);
-	else if (strcmp(key, "cs1") == 0)
+	else if (key == "cs1")
 		portal_group_set_dscp(IPTOS_DSCP_CS1 >> 2);
-	else if (strcmp(key, "cs2") == 0)
+	else if (key == "cs2")
 		portal_group_set_dscp(IPTOS_DSCP_CS2 >> 2);
-	else if (strcmp(key, "cs3") == 0)
+	else if (key == "cs3")
 		portal_group_set_dscp(IPTOS_DSCP_CS3 >> 2);
-	else if (strcmp(key, "cs4") == 0)
+	else if (key == "cs4")
 		portal_group_set_dscp(IPTOS_DSCP_CS4 >> 2);
-	else if (strcmp(key, "cs5") == 0)
+	else if (key == "cs5")
 		portal_group_set_dscp(IPTOS_DSCP_CS5 >> 2);
-	else if (strcmp(key, "cs6") == 0)
+	else if (key == "cs6")
 		portal_group_set_dscp(IPTOS_DSCP_CS6 >> 2);
-	else if (strcmp(key, "cs7") == 0)
+	else if (key == "cs7")
 		portal_group_set_dscp(IPTOS_DSCP_CS7 >> 2);
-	else if (strcmp(key, "af11") == 0)
+	else if (key == "af11")
 		portal_group_set_dscp(IPTOS_DSCP_AF11 >> 2);
-	else if (strcmp(key, "af12") == 0)
+	else if (key == "af12")
 		portal_group_set_dscp(IPTOS_DSCP_AF12 >> 2);
-	else if (strcmp(key, "af13") == 0)
+	else if (key == "af13")
 		portal_group_set_dscp(IPTOS_DSCP_AF13 >> 2);
-	else if (strcmp(key, "af21") == 0)
+	else if (key == "af21")
 		portal_group_set_dscp(IPTOS_DSCP_AF21 >> 2);
-	else if (strcmp(key, "af22") == 0)
+	else if (key == "af22")
 		portal_group_set_dscp(IPTOS_DSCP_AF22 >> 2);
-	else if (strcmp(key, "af23") == 0)
+	else if (key == "af23")
 		portal_group_set_dscp(IPTOS_DSCP_AF23 >> 2);
-	else if (strcmp(key, "af31") == 0)
+	else if (key == "af31")
 		portal_group_set_dscp(IPTOS_DSCP_AF31 >> 2);
-	else if (strcmp(key, "af32") == 0)
+	else if (key == "af32")
 		portal_group_set_dscp(IPTOS_DSCP_AF32 >> 2);
-	else if (strcmp(key, "af33") == 0)
+	else if (key == "af33")
 		portal_group_set_dscp(IPTOS_DSCP_AF33 >> 2);
-	else if (strcmp(key, "af41") == 0)
+	else if (key == "af41")
 		portal_group_set_dscp(IPTOS_DSCP_AF41 >> 2);
-	else if (strcmp(key, "af42") == 0)
+	else if (key == "af42")
 		portal_group_set_dscp(IPTOS_DSCP_AF42 >> 2);
-	else if (strcmp(key, "af43") == 0)
+	else if (key == "af43")
 		portal_group_set_dscp(IPTOS_DSCP_AF43 >> 2);
 	else {
 		log_warnx("\"dscp\" property value is not a supported textual value");
@@ -625,31 +574,27 @@ uclparse_dscp(const char *group_type, const char *pg_name,
 
 static bool
 uclparse_pcp(const char *group_type, const char *pg_name,
-    const ucl_object_t *obj)
+    const ucl::Ucl &obj)
 {
-	if (obj->type != UCL_INT) {
+	if (obj.type() != UCL_INT) {
 		log_warnx("\"pcp\" property of %s group \"%s\" is not an "
 		    "integer", group_type, pg_name);
 		return (false);
 	}
-	return (portal_group_set_pcp(ucl_object_toint(obj)));
+	return (portal_group_set_pcp(obj.int_value()));
 }
 
 static bool
-uclparse_portal_group(const char *name, const ucl_object_t *top)
+uclparse_portal_group(const char *name, const ucl::Ucl &top)
 {
-	ucl_object_iter_t it = NULL, it2 = NULL;
-	const ucl_object_t *obj = NULL, *tmp = NULL;
-	const char *key;
-
 	if (!portal_group_start(name))
 		return (false);
 
-	while ((obj = ucl_iterate_object(top, &it, true))) {
-		key = ucl_object_key(obj);
+	for (const auto &obj : top) {
+		std::string key = obj.key();
 
-		if (strcmp(key, "discovery-auth-group") == 0) {
-			if (obj->type != UCL_STRING) {
+		if (key == "discovery-auth-group") {
+			if (obj.type() != UCL_STRING) {
 				log_warnx("\"discovery-auth-group\" property "
 				    "of portal-group \"%s\" is not a string",
 				    name);
@@ -657,36 +602,36 @@ uclparse_portal_group(const char *name, const ucl_object_t *top)
 			}
 
 			if (!portal_group_set_discovery_auth_group(
-			    ucl_object_tostring(obj)))
+			    obj.string_value().c_str()))
 				goto fail;
 		}
 
-		if (strcmp(key, "discovery-filter") == 0) {
-			if (obj->type != UCL_STRING) {
+		if (key == "discovery-filter") {
+			if (obj.type() != UCL_STRING) {
 				log_warnx("\"discovery-filter\" property of "
 				    "portal-group \"%s\" is not a string",
 				    name);
 				goto fail;
 			}
 
-			if (!portal_group_set_filter(ucl_object_tostring(obj)))
+			if (!portal_group_set_filter(
+			    obj.string_value().c_str()))
 				goto fail;
 		}
 
-		if (strcmp(key, "foreign") == 0) {
+		if (key == "foreign") {
 			portal_group_set_foreign();
 		}
 
-		if (strcmp(key, "listen") == 0) {
-			if (obj->type == UCL_STRING) {
+		if (key == "listen") {
+			if (obj.type() == UCL_STRING) {
 				if (!portal_group_add_listen(
-				    ucl_object_tostring(obj), false))
+				    obj.string_value().c_str(), false))
 					goto fail;
-			} else if (obj->type == UCL_ARRAY) {
-				while ((tmp = ucl_iterate_object(obj, &it2,
-				    true))) {
+			} else if (obj.type() == UCL_ARRAY) {
+				for (const auto &tmp : obj) {
 					if (!portal_group_add_listen(
-					    ucl_object_tostring(tmp),
+					    tmp.string_value().c_str(),
 					    false))
 						goto fail;
 				}
@@ -698,16 +643,15 @@ uclparse_portal_group(const char *name, const ucl_object_t *top)
 			}
 		}
 
-		if (strcmp(key, "listen-iser") == 0) {
-			if (obj->type == UCL_STRING) {
+		if (key == "listen-iser") {
+			if (obj.type() == UCL_STRING) {
 				if (!portal_group_add_listen(
-				    ucl_object_tostring(obj), true))
+				    obj.string_value().c_str(), true))
 					goto fail;
-			} else if (obj->type == UCL_ARRAY) {
-				while ((tmp = ucl_iterate_object(obj, &it2,
-				    true))) {
+			} else if (obj.type() == UCL_ARRAY) {
+				for (const auto &tmp : obj) {
 					if (!portal_group_add_listen(
-					    ucl_object_tostring(tmp),
+					    tmp.string_value().c_str(),
 					    true))
 						goto fail;
 				}
@@ -719,20 +663,21 @@ uclparse_portal_group(const char *name, const ucl_object_t *top)
 			}
 		}
 
-		if (strcmp(key, "offload") == 0) {
-			if (obj->type != UCL_STRING) {
+		if (key == "offload") {
+			if (obj.type() != UCL_STRING) {
 				log_warnx("\"offload\" property of "
 				    "portal-group \"%s\" is not a string",
 				    name);
 				goto fail;
 			}
 
-			if (!portal_group_set_offload(ucl_object_tostring(obj)))
+			if (!portal_group_set_offload(
+			    obj.string_value().c_str()))
 				goto fail;
 		}
 
-		if (strcmp(key, "redirect") == 0) {
-			if (obj->type != UCL_STRING) {
+		if (key == "redirect") {
+			if (obj.type() != UCL_STRING) {
 				log_warnx("\"listen\" property of "
 				    "portal-group \"%s\" is not a string",
 				    name);
@@ -740,43 +685,42 @@ uclparse_portal_group(const char *name, const ucl_object_t *top)
 			}
 
 			if (!portal_group_set_redirection(
-			    ucl_object_tostring(obj)))
+			    obj.string_value().c_str()))
 				goto fail;
 		}
 
-		if (strcmp(key, "options") == 0) {
-			if (obj->type != UCL_OBJECT) {
+		if (key == "options") {
+			if (obj.type() != UCL_OBJECT) {
 				log_warnx("\"options\" property of portal group "
 				    "\"%s\" is not an object", name);
 				goto fail;
 			}
 
-			while ((tmp = ucl_iterate_object(obj, &it2,
-			    true))) {
+			for (const auto &tmp : obj) {
 				if (!portal_group_add_option(
-				    ucl_object_key(tmp),
-				    ucl_object_tostring_forced(tmp)))
+				    tmp.key().c_str(),
+				    tmp.forced_string_value().c_str()))
 					goto fail;
 			}
 		}
 
-		if (strcmp(key, "tag") == 0) {
-			if (obj->type != UCL_INT) {
+		if (key == "tag") {
+			if (obj.type() != UCL_INT) {
 				log_warnx("\"tag\" property of portal group "
 				    "\"%s\" is not an integer",
 				    name);
 				goto fail;
 			}
 
-			portal_group_set_tag(ucl_object_toint(obj));
+			portal_group_set_tag(obj.int_value());
 		}
 
-		if (strcmp(key, "dscp") == 0) {
+		if (key == "dscp") {
 			if (!uclparse_dscp("portal", name, obj))
 				goto fail;
 		}
 
-		if (strcmp(key, "pcp") == 0) {
+		if (key == "pcp") {
 			if (!uclparse_pcp("portal", name, obj))
 				goto fail;
 		}
@@ -790,58 +734,53 @@ fail:
 }
 
 static bool
-uclparse_target(const char *name, const ucl_object_t *top)
+uclparse_target(const char *name, const ucl::Ucl &top)
 {
-	ucl_object_iter_t it = NULL, it2 = NULL;
-	const ucl_object_t *obj = NULL, *tmp = NULL;
-	const char *key;
-
 	if (!target_start(name))
 		return (false);
 
*** 380 LINES SKIPPED ***