Add an additional, libucl-based configuration file parser to ctld.

Default ctld behavior remains unchanged - libucl parser can be selected
explicitly by adding -u switch to ctld command line.

Reviewed by:	trasz
Approved by:	trasz (mentor)
MFC after:	1 month
Relnotes:	yes
Sponsored by:	iXsystems, Inc.
Differential Revision:	https://reviews.freebsd.org/D4534
This commit is contained in:
Jakub Wojciech Klama 2016-02-03 15:45:13 +00:00
parent 1485c016ba
commit 5c734b0410
5 changed files with 1023 additions and 93 deletions

View File

@ -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

View File

@ -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");

View File

@ -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);

View File

@ -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);
}

900
usr.sbin/ctld/uclparse.c Normal file
View File

@ -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);
}