summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/ctld/Makefile7
-rw-r--r--usr.sbin/ctld/ctld.c112
-rw-r--r--usr.sbin/ctld/ctld.h4
-rw-r--r--usr.sbin/ctld/parse.y93
-rw-r--r--usr.sbin/ctld/uclparse.c900
5 files changed, 1023 insertions, 93 deletions
diff --git a/usr.sbin/ctld/Makefile b/usr.sbin/ctld/Makefile
index 6169d308ef2c..664dc22efbb1 100644
--- a/usr.sbin/ctld/Makefile
+++ b/usr.sbin/ctld/Makefile
@@ -1,8 +1,11 @@
# $FreeBSD$
+CFLAGS+=-I${.CURDIR}/../../contrib/libucl/include
+.PATH: ${.CURDIR}/../../contrib/libucl/include
+
PROG= ctld
SRCS= chap.c ctld.c discovery.c isns.c kernel.c keys.c log.c
-SRCS+= login.c parse.y pdu.c token.l y.tab.h
+SRCS+= login.c parse.y pdu.c token.l y.tab.h uclparse.c
CFLAGS+= -I${.CURDIR}
CFLAGS+= -I${.CURDIR}/../../sys
CFLAGS+= -I${.CURDIR}/../../sys/cam/ctl
@@ -10,7 +13,7 @@ CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi
#CFLAGS+= -DICL_KERNEL_PROXY
MAN= ctld.8 ctl.conf.5
-LIBADD= bsdxml l md sbuf util
+LIBADD= bsdxml l md sbuf util ucl m
YFLAGS+= -v
CLEANFILES= y.tab.c y.tab.h y.output
diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c
index 92fa5539f7f6..6c8b4a89fe93 100644
--- a/usr.sbin/ctld/ctld.c
+++ b/usr.sbin/ctld/ctld.c
@@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -2491,6 +2492,104 @@ register_signals(void)
log_err(1, "sigaction");
}
+static void
+check_perms(const char *path)
+{
+ struct stat sb;
+ int error;
+
+ error = stat(path, &sb);
+ if (error != 0) {
+ log_warn("stat");
+ return;
+ }
+ if (sb.st_mode & S_IWOTH) {
+ log_warnx("%s is world-writable", path);
+ } else if (sb.st_mode & S_IROTH) {
+ log_warnx("%s is world-readable", path);
+ } else if (sb.st_mode & S_IXOTH) {
+ /*
+ * Ok, this one doesn't matter, but still do it,
+ * just for consistency.
+ */
+ log_warnx("%s is world-executable", path);
+ }
+
+ /*
+ * XXX: Should we also check for owner != 0?
+ */
+}
+
+static struct conf *
+conf_new_from_file(const char *path, struct conf *oldconf, bool ucl)
+{
+ struct conf *conf;
+ struct auth_group *ag;
+ struct portal_group *pg;
+ struct pport *pp;
+ int error;
+
+ log_debugx("obtaining configuration from %s", path);
+
+ conf = conf_new();
+
+ TAILQ_FOREACH(pp, &oldconf->conf_pports, pp_next)
+ pport_copy(pp, conf);
+
+ ag = auth_group_new(conf, "default");
+ assert(ag != NULL);
+
+ ag = auth_group_new(conf, "no-authentication");
+ assert(ag != NULL);
+ ag->ag_type = AG_TYPE_NO_AUTHENTICATION;
+
+ ag = auth_group_new(conf, "no-access");
+ assert(ag != NULL);
+ ag->ag_type = AG_TYPE_DENY;
+
+ pg = portal_group_new(conf, "default");
+ assert(pg != NULL);
+
+ if (ucl)
+ error = uclparse_conf(conf, path);
+ else
+ error = parse_conf(conf, path);
+
+ if (error != 0) {
+ conf_delete(conf);
+ return (NULL);
+ }
+
+ check_perms(path);
+
+ if (conf->conf_default_ag_defined == false) {
+ log_debugx("auth-group \"default\" not defined; "
+ "going with defaults");
+ ag = auth_group_find(conf, "default");
+ assert(ag != NULL);
+ ag->ag_type = AG_TYPE_DENY;
+ }
+
+ if (conf->conf_default_pg_defined == false) {
+ log_debugx("portal-group \"default\" not defined; "
+ "going with defaults");
+ pg = portal_group_find(conf, "default");
+ assert(pg != NULL);
+ portal_group_add_listen(pg, "0.0.0.0:3260", false);
+ portal_group_add_listen(pg, "[::]:3260", false);
+ }
+
+ conf->conf_kernel_port_on = true;
+
+ error = conf_verify(conf);
+ if (error != 0) {
+ conf_delete(conf);
+ return (NULL);
+ }
+
+ return (conf);
+}
+
int
main(int argc, char **argv)
{
@@ -2499,13 +2598,17 @@ main(int argc, char **argv)
const char *config_path = DEFAULT_CONFIG_PATH;
int debug = 0, ch, error;
bool dont_daemonize = false;
+ bool use_ucl = false;
- while ((ch = getopt(argc, argv, "df:R")) != -1) {
+ while ((ch = getopt(argc, argv, "duf:R")) != -1) {
switch (ch) {
case 'd':
dont_daemonize = true;
debug++;
break;
+ case 'u':
+ use_ucl = true;
+ break;
case 'f':
config_path = optarg;
break;
@@ -2529,7 +2632,8 @@ main(int argc, char **argv)
kernel_init();
oldconf = conf_new_from_kernel();
- newconf = conf_new_from_file(config_path, oldconf);
+ newconf = conf_new_from_file(config_path, oldconf, use_ucl);
+
if (newconf == NULL)
log_errx(1, "configuration error; exiting");
if (debug > 0) {
@@ -2564,7 +2668,9 @@ main(int argc, char **argv)
if (sighup_received) {
sighup_received = false;
log_debugx("received SIGHUP, reloading configuration");
- tmpconf = conf_new_from_file(config_path, newconf);
+ tmpconf = conf_new_from_file(config_path, newconf,
+ use_ucl);
+
if (tmpconf == NULL) {
log_warnx("configuration error, "
"continuing with old configuration");
diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h
index 808b722aa542..e9152329121f 100644
--- a/usr.sbin/ctld/ctld.h
+++ b/usr.sbin/ctld/ctld.h
@@ -297,8 +297,10 @@ int rchap_receive(struct rchap *rchap,
char *rchap_get_response(struct rchap *rchap);
void rchap_delete(struct rchap *rchap);
+int parse_conf(struct conf *conf, const char *path);
+int uclparse_conf(struct conf *conf, const char *path);
+
struct conf *conf_new(void);
-struct conf *conf_new_from_file(const char *path, struct conf *old);
struct conf *conf_new_from_kernel(void);
void conf_delete(struct conf *conf);
int conf_verify(struct conf *conf);
diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y
index afbf315bf02d..820b4c7d4bc1 100644
--- a/usr.sbin/ctld/parse.y
+++ b/usr.sbin/ctld/parse.y
@@ -1044,70 +1044,18 @@ yyerror(const char *str)
lineno, yytext, str);
}
-static void
-check_perms(const char *path)
+int
+parse_conf(struct conf *newconf, const char *path)
{
- struct stat sb;
int error;
- error = stat(path, &sb);
- if (error != 0) {
- log_warn("stat");
- return;
- }
- if (sb.st_mode & S_IWOTH) {
- log_warnx("%s is world-writable", path);
- } else if (sb.st_mode & S_IROTH) {
- log_warnx("%s is world-readable", path);
- } else if (sb.st_mode & S_IXOTH) {
- /*
- * Ok, this one doesn't matter, but still do it,
- * just for consistency.
- */
- log_warnx("%s is world-executable", path);
- }
-
- /*
- * XXX: Should we also check for owner != 0?
- */
-}
-
-struct conf *
-conf_new_from_file(const char *path, struct conf *oldconf)
-{
- struct auth_group *ag;
- struct portal_group *pg;
- struct pport *pp;
- int error;
-
- log_debugx("obtaining configuration from %s", path);
-
- conf = conf_new();
-
- TAILQ_FOREACH(pp, &oldconf->conf_pports, pp_next)
- pport_copy(pp, conf);
-
- ag = auth_group_new(conf, "default");
- assert(ag != NULL);
-
- ag = auth_group_new(conf, "no-authentication");
- assert(ag != NULL);
- ag->ag_type = AG_TYPE_NO_AUTHENTICATION;
-
- ag = auth_group_new(conf, "no-access");
- assert(ag != NULL);
- ag->ag_type = AG_TYPE_DENY;
-
- pg = portal_group_new(conf, "default");
- assert(pg != NULL);
-
+ conf = newconf;
yyin = fopen(path, "r");
if (yyin == NULL) {
log_warn("unable to open configuration file %s", path);
- conf_delete(conf);
- return (NULL);
+ return (1);
}
- check_perms(path);
+
lineno = 1;
yyrestart(yyin);
error = yyparse();
@@ -1116,35 +1064,6 @@ conf_new_from_file(const char *path, struct conf *oldconf)
target = NULL;
lun = NULL;
fclose(yyin);
- if (error != 0) {
- conf_delete(conf);
- return (NULL);
- }
-
- if (conf->conf_default_ag_defined == false) {
- log_debugx("auth-group \"default\" not defined; "
- "going with defaults");
- ag = auth_group_find(conf, "default");
- assert(ag != NULL);
- ag->ag_type = AG_TYPE_DENY;
- }
-
- if (conf->conf_default_pg_defined == false) {
- log_debugx("portal-group \"default\" not defined; "
- "going with defaults");
- pg = portal_group_find(conf, "default");
- assert(pg != NULL);
- portal_group_add_listen(pg, "0.0.0.0:3260", false);
- portal_group_add_listen(pg, "[::]:3260", false);
- }
-
- conf->conf_kernel_port_on = true;
-
- error = conf_verify(conf);
- if (error != 0) {
- conf_delete(conf);
- return (NULL);
- }
- return (conf);
+ return (error);
}
diff --git a/usr.sbin/ctld/uclparse.c b/usr.sbin/ctld/uclparse.c
new file mode 100644
index 000000000000..fb2cce0a144e
--- /dev/null
+++ b/usr.sbin/ctld/uclparse.c
@@ -0,0 +1,900 @@
+/*-
+ * Copyright (c) 2015 iXsystems Inc.
+ * All rights reserved.
+ *
+ * This software was developed by Jakub Klama <jceel@FreeBSD.org>
+ * under sponsorship from iXsystems Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ucl.h>
+
+#include "ctld.h"
+
+static struct conf *conf = NULL;
+
+static int uclparse_toplevel(const ucl_object_t *);
+static int uclparse_chap(struct auth_group *, const ucl_object_t *);
+static int uclparse_chap_mutual(struct auth_group *, const ucl_object_t *);
+static int uclparse_lun(const char *, const ucl_object_t *);
+static int uclparse_auth_group(const char *, const ucl_object_t *);
+static int uclparse_portal_group(const char *, const ucl_object_t *);
+static int uclparse_target(const char *, const ucl_object_t *);
+static int uclparse_target_portal_group(struct target *, const ucl_object_t *);
+static int uclparse_target_lun(struct target *, const ucl_object_t *);
+
+static int
+uclparse_chap(struct auth_group *auth_group, const ucl_object_t *obj)
+{
+ const struct auth *ca;
+ const ucl_object_t *user, *secret;
+
+ user = ucl_object_find_key(obj, "user");
+ if (!user || user->type != UCL_STRING) {
+ log_warnx("chap section in auth-group \"%s\" is missing "
+ "\"user\" string key", auth_group->ag_name);
+ return (1);
+ }
+
+ secret = ucl_object_find_key(obj, "secret");
+ if (!secret || secret->type != UCL_STRING) {
+ log_warnx("chap section in auth-group \"%s\" is missing "
+ "\"secret\" string key", auth_group->ag_name);
+ }
+
+ ca = auth_new_chap(auth_group,
+ ucl_object_tostring(user),
+ ucl_object_tostring(secret));
+
+ if (ca == NULL)
+ return (1);
+
+ return (0);
+}
+
+static int
+uclparse_chap_mutual(struct auth_group *auth_group, const ucl_object_t *obj)
+{
+ const struct auth *ca;
+ 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) {
+ log_warnx("chap-mutual section in auth-group \"%s\" is missing "
+ "\"user\" string key", auth_group->ag_name);
+ return (1);
+ }
+
+ secret = ucl_object_find_key(obj, "secret");
+ if (!secret || secret->type != UCL_STRING) {
+ log_warnx("chap-mutual section in auth-group \"%s\" is missing "
+ "\"secret\" string key", auth_group->ag_name);
+ return (1);
+ }
+
+ mutual_user = ucl_object_find_key(obj, "mutual-user");
+ if (!user || user->type != UCL_STRING) {
+ log_warnx("chap-mutual section in auth-group \"%s\" is missing "
+ "\"mutual-user\" string key", auth_group->ag_name);
+ return (1);
+ }
+
+ mutual_secret = ucl_object_find_key(obj, "mutual-secret");
+ if (!secret || secret->type != UCL_STRING) {
+ log_warnx("chap-mutual section in auth-group \"%s\" is missing "
+ "\"mutual-secret\" string key", auth_group->ag_name);
+ return (1);
+ }
+
+ ca = auth_new_chap_mutual(auth_group,
+ ucl_object_tostring(user),
+ ucl_object_tostring(secret),
+ ucl_object_tostring(mutual_user),
+ ucl_object_tostring(mutual_secret));
+
+ if (ca == NULL)
+ return (1);
+
+ return (0);
+}
+
+static int
+uclparse_target_portal_group(struct target *target, const ucl_object_t *obj)
+{
+ struct portal_group *tpg;
+ struct auth_group *tag = NULL;
+ struct port *tp;
+ const ucl_object_t *portal_group, *auth_group;
+
+ portal_group = ucl_object_find_key(obj, "name");
+ if (!portal_group || portal_group->type != UCL_STRING) {
+ log_warnx("portal-group section in target \"%s\" is missing "
+ "\"name\" string key", target->t_name);
+ return (1);
+ }
+
+ auth_group = ucl_object_find_key(obj, "auth-group-name");
+ if (auth_group && auth_group->type != UCL_STRING) {
+ log_warnx("portal-group section in target \"%s\" is missing "
+ "\"auth-group-name\" string key", target->t_name);
+ return (1);
+ }
+
+
+ tpg = portal_group_find(conf, ucl_object_tostring(portal_group));
+ if (tpg == NULL) {
+ log_warnx("unknown portal-group \"%s\" for target "
+ "\"%s\"", ucl_object_tostring(portal_group), target->t_name);
+ return (1);
+ }
+
+ if (auth_group) {
+ tag = auth_group_find(conf, ucl_object_tostring(auth_group));
+ if (tag == NULL) {
+ log_warnx("unknown auth-group \"%s\" for target "
+ "\"%s\"", ucl_object_tostring(auth_group),
+ target->t_name);
+ return (1);
+ }
+ }
+
+ tp = port_new(conf, target, tpg);
+ if (tp == NULL) {
+ log_warnx("can't link portal-group \"%s\" to target "
+ "\"%s\"", ucl_object_tostring(portal_group), target->t_name);
+ return (1);
+ }
+ tp->p_auth_group = tag;
+
+ return (0);
+}
+
+static int
+uclparse_target_lun(struct target *target, const ucl_object_t *obj)
+{
+ struct lun *lun;
+
+ if (obj->type == UCL_INT) {
+ char *name;
+
+ asprintf(&name, "%s,lun,%ju", target->t_name,
+ ucl_object_toint(obj));
+ lun = lun_new(conf, name);
+ if (lun == NULL)
+ return (1);
+
+ lun_set_scsiname(lun, name);
+ target->t_luns[ucl_object_toint(obj)] = lun;
+ return (0);
+ }
+
+ if (obj->type == UCL_OBJECT) {
+ const ucl_object_t *num = ucl_object_find_key(obj, "number");
+ const ucl_object_t *name = ucl_object_find_key(obj, "name");
+
+ if (num == NULL || num->type != UCL_INT) {
+ log_warnx("lun section in target \"%s\" is missing "
+ "\"number\" integer property", target->t_name);
+ return (1);
+ }
+
+ if (name == NULL || name->type != UCL_STRING) {
+ log_warnx("lun section in target \"%s\" is missing "
+ "\"name\" string property", target->t_name);
+ return (1);
+ }
+
+ lun = lun_find(conf, ucl_object_tostring(name));
+ if (lun == NULL)
+ return (1);
+
+ target->t_luns[ucl_object_toint(num)] = lun;
+ }
+
+ return (0);
+}
+
+static int
+uclparse_toplevel(const ucl_object_t *top)
+{
+ ucl_object_iter_t it = NULL, iter = NULL;
+ const ucl_object_t *obj = NULL, *child = NULL;
+ int err = 0;
+
+ /* Pass 1 - everything except targets */
+ while ((obj = ucl_iterate_object(top, &it, true))) {
+ const char *key = ucl_object_key(obj);
+
+ if (!strcmp(key, "debug")) {
+ if (obj->type == UCL_INT)
+ conf->conf_debug = ucl_object_toint(obj);
+ else {
+ log_warnx("\"debug\" property value is not integer");
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "timeout")) {
+ if (obj->type == UCL_INT)
+ conf->conf_timeout = ucl_object_toint(obj);
+ else {
+ log_warnx("\"timeout\" property value is not integer");
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "maxproc")) {
+ if (obj->type == UCL_INT)
+ conf->conf_maxproc = ucl_object_toint(obj);
+ else {
+ log_warnx("\"maxproc\" property value is not integer");
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "pidfile")) {
+ if (obj->type == UCL_STRING)
+ conf->conf_pidfile_path = strdup(
+ ucl_object_tostring(obj));
+ else {
+ log_warnx("\"pidfile\" property value is not string");
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "isns-server")) {
+ if (obj->type == UCL_ARRAY) {
+ iter = NULL;
+ while ((child = ucl_iterate_object(obj, &iter,
+ true))) {
+ if (child->type != UCL_STRING)
+ return (1);
+
+ err = isns_new(conf,
+ ucl_object_tostring(child));
+ if (err != 0) {
+ return (1);
+ }
+ }
+ } else {
+ log_warnx("\"isns-server\" property value is "
+ "not an array");
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "isns-period")) {
+ if (obj->type == UCL_INT)
+ conf->conf_timeout = ucl_object_toint(obj);
+ else {
+ log_warnx("\"isns-period\" property value is not integer");
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "isns-timeout")) {
+ if (obj->type == UCL_INT)
+ conf->conf_timeout = ucl_object_toint(obj);
+ else {
+ log_warnx("\"isns-timeout\" property value is not integer");
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "auth-group")) {
+ if (obj->type == UCL_OBJECT) {
+ iter = NULL;
+ while ((child = ucl_iterate_object(obj, &iter, true))) {
+ uclparse_auth_group(ucl_object_key(child), child);
+ }
+ } else {
+ log_warnx("\"auth-group\" section is not an object");
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "portal-group")) {
+ if (obj->type == UCL_OBJECT) {
+ iter = NULL;
+ while ((child = ucl_iterate_object(obj, &iter, true))) {
+ uclparse_portal_group(ucl_object_key(child), child);
+ }
+ } else {
+ log_warnx("\"portal-group\" section is not an object");
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "lun")) {
+ if (obj->type == UCL_OBJECT) {
+ iter = NULL;
+ while ((child = ucl_iterate_object(obj, &iter, true))) {
+ uclparse_lun(ucl_object_key(child), child);
+ }
+ } else {
+ log_warnx("\"lun\" section is not an object");
+ return (1);
+ }
+ }
+ }
+
+ /* Pass 2 - targets */
+ it = NULL;
+ while ((obj = ucl_iterate_object(top, &it, true))) {
+ const char *key = ucl_object_key(obj);
+
+ if (!strcmp(key, "target")) {
+ if (obj->type == UCL_OBJECT) {
+ iter = NULL;
+ while ((child = ucl_iterate_object(obj, &iter,
+ true))) {
+ uclparse_target(ucl_object_key(child),
+ child);
+ }
+ } else {
+ log_warnx("\"target\" section is not an object");
+ return (1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static int
+uclparse_auth_group(const char *name, const ucl_object_t *top)
+{
+ struct auth_group *auth_group;
+ const struct auth_name *an;
+ const struct auth_portal *ap;
+ ucl_object_iter_t it = NULL, it2 = NULL;
+ const ucl_object_t *obj = NULL, *tmp = NULL;
+ const char *key;
+ int err;
+
+ if (!strcmp(name, "default") &&
+ conf->conf_default_ag_defined == false) {
+ auth_group = auth_group_find(conf, name);
+ conf->conf_default_ag_defined = true;
+ } else {
+ auth_group = auth_group_new(conf, name);
+ }
+
+ if (auth_group == NULL)
+ return (1);
+
+ while ((obj = ucl_iterate_object(top, &it, true))) {
+ key = ucl_object_key(obj);
+
+ if (!strcmp(key, "auth-type")) {
+ const char *value = ucl_object_tostring(obj);
+
+ err = auth_group_set_type(auth_group, value);
+ if (err)
+ return (1);
+ }
+
+ if (!strcmp(key, "chap")) {
+ if (obj->type != UCL_ARRAY) {
+ log_warnx("\"chap\" property of "
+ "auth-group \"%s\" is not an array",
+ name);
+ return (1);
+ }
+
+ it2 = NULL;
+ while ((tmp = ucl_iterate_object(obj, &it2, true))) {
+ if (uclparse_chap(auth_group, tmp) != 0)
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "chap-mutual")) {
+ if (obj->type != UCL_ARRAY) {
+ log_warnx("\"chap-mutual\" property of "
+ "auth-group \"%s\" is not an array",
+ name);
+ return (1);
+ }
+
+ it2 = NULL;
+ while ((tmp = ucl_iterate_object(obj, &it2, true))) {
+ if (uclparse_chap_mutual(auth_group, tmp) != 0)
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "initiator-name")) {
+ if (obj->type != UCL_ARRAY) {
+ log_warnx("\"initiator-name\" property of "
+ "auth-group \"%s\" is not an array",
+ name);
+ return (1);
+ }
+
+ it2 = NULL;
+ while ((tmp = ucl_iterate_object(obj, &it2, true))) {
+ const char *value = ucl_object_tostring(tmp);
+
+ an = auth_name_new(auth_group, value);
+ if (an == NULL)
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "initiator-portal")) {
+ if (obj->type != UCL_ARRAY) {
+ log_warnx("\"initiator-portal\" property of "
+ "auth-group \"%s\" is not an array",
+ name);
+ return (1);
+ }
+
+ it2 = NULL;
+ while ((tmp = ucl_iterate_object(obj, &it2, true))) {
+ const char *value = ucl_object_tostring(tmp);
+
+ ap = auth_portal_new(auth_group, value);
+ if (ap == NULL)
+ return (1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static int
+uclparse_portal_group(const char *name, const ucl_object_t *top)
+{
+ struct portal_group *portal_group;
+ ucl_object_iter_t it = NULL, it2 = NULL;
+ const ucl_object_t *obj = NULL, *tmp = NULL;
+ const char *key;
+
+ if (strcmp(name, "default") == 0 &&
+ conf->conf_default_pg_defined == false) {
+ portal_group = portal_group_find(conf, name);
+ conf->conf_default_pg_defined = true;
+ } else {
+ portal_group = portal_group_new(conf, name);
+ }
+
+ if (portal_group == NULL)
+ return (1);
+
+ while ((obj = ucl_iterate_object(top, &it, true))) {
+ key = ucl_object_key(obj);
+
+ if (!strcmp(key, "discovery-auth-group")) {
+ portal_group->pg_discovery_auth_group =
+ auth_group_find(conf, ucl_object_tostring(obj));
+ if (portal_group->pg_discovery_auth_group == NULL) {
+ log_warnx("unknown discovery-auth-group \"%s\" "
+ "for portal-group \"%s\"",
+ ucl_object_tostring(obj),
+ portal_group->pg_name);
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "discovery-filter")) {
+ if (obj->type != UCL_STRING) {
+ log_warnx("\"discovery-filter\" property of "
+ "portal-group \"%s\" is not a string",
+ portal_group->pg_name);
+ return (1);
+ }
+
+ if (portal_group_set_filter(portal_group,
+ ucl_object_tostring(obj)) != 0)
+ return (1);
+ }
+
+ if (!strcmp(key, "listen")) {
+ if (obj->type == UCL_STRING) {
+ if (portal_group_add_listen(portal_group,
+ ucl_object_tostring(obj), false) != 0)
+ return (1);
+ } else if (obj->type == UCL_ARRAY) {
+ while ((tmp = ucl_iterate_object(obj, &it2,
+ true))) {
+ if (portal_group_add_listen(
+ portal_group,
+ ucl_object_tostring(tmp),
+ false) != 0)
+ return (1);
+ }
+ } else {
+ log_warnx("\"listen\" property of "
+ "portal-group \"%s\" is not a string",
+ portal_group->pg_name);
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "listen-iser")) {
+ if (obj->type == UCL_STRING) {
+ if (portal_group_add_listen(portal_group,
+ ucl_object_tostring(obj), true) != 0)
+ return (1);
+ } else if (obj->type == UCL_ARRAY) {
+ while ((tmp = ucl_iterate_object(obj, &it2,
+ true))) {
+ if (portal_group_add_listen(
+ portal_group,
+ ucl_object_tostring(tmp),
+ true) != 0)
+ return (1);
+ }
+ } else {
+ log_warnx("\"listen\" property of "
+ "portal-group \"%s\" is not a string",
+ portal_group->pg_name);
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "redirect")) {
+ if (obj->type != UCL_STRING) {
+ log_warnx("\"listen\" property of "
+ "portal-group \"%s\" is not a string",
+ portal_group->pg_name);
+ return (1);
+ }
+
+ if (portal_group_set_redirection(portal_group,
+ ucl_object_tostring(obj)) != 0)
+ return (1);
+ }
+
+ if (!strcmp(key, "options")) {
+ if (obj->type != UCL_OBJECT) {
+ log_warnx("\"options\" property of portal group "
+ "\"%s\" is not an object", portal_group->pg_name);
+ return (1);
+ }
+
+ while ((tmp = ucl_iterate_object(obj, &it2,
+ true))) {
+ option_new(&portal_group->pg_options,
+ ucl_object_key(tmp),
+ ucl_object_tostring_forced(tmp));
+ }
+ }
+ }
+
+ return (0);
+}
+
+static int
+uclparse_target(const char *name, const ucl_object_t *top)
+{
+ struct target *target;
+ ucl_object_iter_t it = NULL, it2 = NULL;
+ const ucl_object_t *obj = NULL, *tmp = NULL;
+ const char *key;
+
+ target = target_new(conf, name);
+
+ while ((obj = ucl_iterate_object(top, &it, true))) {
+ key = ucl_object_key(obj);
+
+ if (!strcmp(key, "alias")) {
+ if (obj->type != UCL_STRING) {
+ log_warnx("\"alias\" property of target "
+ "\"%s\" is not a string", target->t_name);
+ return (1);
+ }
+
+ target->t_alias = strdup(ucl_object_tostring(obj));
+ }
+
+ if (!strcmp(key, "auth-group")) {
+ if (target->t_auth_group != NULL) {
+ if (target->t_auth_group->ag_name != NULL)
+ log_warnx("auth-group for target \"%s\" "
+ "specified more than once",
+ target->t_name);
+ else
+ log_warnx("cannot use both auth-group "
+ "and explicit authorisations for "
+ "target \"%s\"", target->t_name);
+ return (1);
+ }
+ target->t_auth_group = auth_group_find(conf,
+ ucl_object_tostring(obj));
+ if (target->t_auth_group == NULL) {
+ log_warnx("unknown auth-group \"%s\" for target "
+ "\"%s\"", ucl_object_tostring(obj),
+ target->t_name);
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "auth-type")) {
+ int error;
+
+ if (target->t_auth_group != NULL) {
+ if (target->t_auth_group->ag_name != NULL) {
+ log_warnx("cannot use both auth-group and "
+ "auth-type for target \"%s\"",
+ target->t_name);
+ return (1);
+ }
+ } else {
+ target->t_auth_group = auth_group_new(conf, NULL);
+ if (target->t_auth_group == NULL)
+ return (1);
+
+ target->t_auth_group->ag_target = target;
+ }
+ error = auth_group_set_type(target->t_auth_group,
+ ucl_object_tostring(obj));
+ if (error != 0)
+ return (1);
+ }
+
+ if (!strcmp(key, "chap")) {
+ if (uclparse_chap(target->t_auth_group, obj) != 0)
+ return (1);
+ }
+
+ if (!strcmp(key, "chap-mutual")) {
+ if (uclparse_chap_mutual(target->t_auth_group, obj) != 0)
+ return (1);
+ }
+
+ if (!strcmp(key, "initiator-name")) {
+ const struct auth_name *an;
+
+ if (target->t_auth_group != NULL) {
+ if (target->t_auth_group->ag_name != NULL) {
+ log_warnx("cannot use both auth-group and "
+ "initiator-name for target \"%s\"",
+ target->t_name);
+ return (1);
+ }
+ } else {
+ target->t_auth_group = auth_group_new(conf, NULL);
+ if (target->t_auth_group == NULL)
+ return (1);
+
+ target->t_auth_group->ag_target = target;
+ }
+ an = auth_name_new(target->t_auth_group,
+ ucl_object_tostring(obj));
+ if (an == NULL)
+ return (1);
+ }
+
+ if (!strcmp(key, "initiator-portal")) {
+ const struct auth_portal *ap;
+
+ if (target->t_auth_group != NULL) {
+ if (target->t_auth_group->ag_name != NULL) {
+ log_warnx("cannot use both auth-group and "
+ "initiator-portal for target \"%s\"",
+ target->t_name);
+ return (1);
+ }
+ } else {
+ target->t_auth_group = auth_group_new(conf, NULL);
+ if (target->t_auth_group == NULL)
+ return (1);
+
+ target->t_auth_group->ag_target = target;
+ }
+ ap = auth_portal_new(target->t_auth_group,
+ ucl_object_tostring(obj));
+ if (ap == NULL)
+ return (1);
+ }
+
+ if (!strcmp(key, "portal-group")) {
+ if (obj->type == UCL_OBJECT) {
+ if (uclparse_target_portal_group(target, obj) != 0)
+ return (1);
+ }
+
+ if (obj->type == UCL_ARRAY) {
+ while ((tmp = ucl_iterate_object(obj, &it2,
+ true))) {
+ if (uclparse_target_portal_group(target,
+ tmp) != 0)
+ return (1);
+ }
+ }
+ }
+
+ if (!strcmp(key, "port")) {
+ struct pport *pp;
+ struct port *tp;
+ const char *value = ucl_object_tostring(obj);
+
+ pp = pport_find(conf, value);
+ if (pp == NULL) {
+ log_warnx("unknown port \"%s\" for target \"%s\"",
+ value, target->t_name);
+ return (1);
+ }
+ if (!TAILQ_EMPTY(&pp->pp_ports)) {
+ log_warnx("can't link port \"%s\" to target \"%s\", "
+ "port already linked to some target",
+ value, target->t_name);
+ return (1);
+ }
+ tp = port_new_pp(conf, target, pp);
+ if (tp == NULL) {
+ log_warnx("can't link port \"%s\" to target \"%s\"",
+ value, target->t_name);
+ return (1);
+ }
+ }
+
+ if (!strcmp(key, "redirect")) {
+ if (obj->type != UCL_STRING) {
+ log_warnx("\"redirect\" property of target "
+ "\"%s\" is not a string", target->t_name);
+ return (1);
+ }
+
+ if (target_set_redirection(target,
+ ucl_object_tostring(obj)) != 0)
+ return (1);
+ }
+
+ if (!strcmp(key, "lun")) {
+ while ((tmp = ucl_iterate_object(obj, &it2, true))) {
+ if (uclparse_target_lun(target, tmp) != 0)
+ return (1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static int
+uclparse_lun(const char *name, const ucl_object_t *top)
+{
+ struct lun *lun;
+ ucl_object_iter_t it = NULL, child_it = NULL;
+ const ucl_object_t *obj = NULL, *child = NULL;
+ const char *key;
+
+ lun = lun_new(conf, name);
+
+ while ((obj = ucl_iterate_object(top, &it, true))) {
+ key = ucl_object_key(obj);
+
+ if (!strcmp(key, "backend")) {
+ if (obj->type != UCL_STRING) {
+ log_warnx("\"backend\" property of lun "
+ "\"%s\" is not a string",
+ lun->l_name);
+ return (1);
+ }
+
+ lun_set_backend(lun, ucl_object_tostring(obj));
+ }
+
+ if (!strcmp(key, "blocksize")) {
+ if (obj->type != UCL_INT) {
+ log_warnx("\"blocksize\" property of lun "
+ "\"%s\" is not an integer", lun->l_name);
+ return (1);
+ }
+
+ lun_set_blocksize(lun, ucl_object_toint(obj));
+ }
+
+ if (!strcmp(key, "device-id")) {
+ if (obj->type != UCL_STRING) {
+ log_warnx("\"device-id\" property of lun "
+ "\"%s\" is not an integer", lun->l_name);
+ return (1);
+ }
+
+ lun_set_device_id(lun, ucl_object_tostring(obj));
+ }
+
+ if (!strcmp(key, "options")) {
+ if (obj->type != UCL_OBJECT) {
+ log_warnx("\"options\" property of lun "
+ "\"%s\" is not an object", lun->l_name);
+ return (1);
+ }
+
+ while ((child = ucl_iterate_object(obj, &child_it,
+ true))) {
+ option_new(&lun->l_options,
+ ucl_object_key(child),
+ ucl_object_tostring_forced(child));
+ }
+ }
+
+ if (!strcmp(key, "path")) {
+ if (obj->type != UCL_STRING) {
+ log_warnx("\"path\" property of lun "
+ "\"%s\" is not a string", lun->l_name);
+ return (1);
+ }
+
+ lun_set_path(lun, ucl_object_tostring(obj));
+ }
+
+ if (!strcmp(key, "serial")) {
+ if (obj->type != UCL_STRING) {
+ log_warnx("\"serial\" property of lun "
+ "\"%s\" is not a string", lun->l_name);
+ return (1);
+ }
+
+ lun_set_serial(lun, ucl_object_tostring(obj));
+ }
+
+ if (!strcmp(key, "size")) {
+ if (obj->type != UCL_INT) {
+ log_warnx("\"size\" property of lun "
+ "\"%s\" is not an integer", lun->l_name);
+ return (1);
+ }
+
+ lun_set_size(lun, ucl_object_toint(obj));
+ }
+ }
+
+ return (0);
+}
+
+int
+uclparse_conf(struct conf *newconf, const char *path)
+{
+ struct ucl_parser *parser;
+ int error;
+
+ conf = newconf;
+ parser = ucl_parser_new(0);
+ ucl_parser_add_file(parser, path);
+
+ if (ucl_parser_get_error(parser)) {
+ log_warn("unable to parse configuration file %s: %s", path,
+ ucl_parser_get_error(parser));
+ return (1);
+ }
+
+ error = uclparse_toplevel(ucl_parser_get_object(parser));
+
+ return (error);
+}