1/*-
2 * Copyright (c) 2015 iXsystems Inc.
3 * All rights reserved.
4 *
5 * This software was developed by Jakub Klama <jceel@FreeBSD.org>
6 * under sponsorship from iXsystems Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: releng/11.0/usr.sbin/ctld/uclparse.c 296808 2016-03-13 19:50:17Z jceel $
30 */
31
32#include <sys/queue.h>
33#include <sys/types.h>
34#include <assert.h>
35#include <stdio.h>
36#include <stdint.h>
37#include <stdlib.h>
38#include <string.h>
39#include <ucl.h>
40
41#include "ctld.h"
42
43static struct conf *conf = NULL;
44
45static int uclparse_toplevel(const ucl_object_t *);
46static int uclparse_chap(struct auth_group *, const ucl_object_t *);
47static int uclparse_chap_mutual(struct auth_group *, const ucl_object_t *);
48static int uclparse_lun(const char *, const ucl_object_t *);
49static int uclparse_auth_group(const char *, const ucl_object_t *);
50static int uclparse_portal_group(const char *, const ucl_object_t *);
51static int uclparse_target(const char *, const ucl_object_t *);
52static int uclparse_target_portal_group(struct target *, const ucl_object_t *);
53static int uclparse_target_lun(struct target *, const ucl_object_t *);
54
55static int
56uclparse_chap(struct auth_group *auth_group, const ucl_object_t *obj)
57{
58	const struct auth *ca;
59	const ucl_object_t *user, *secret;
60
61	user = ucl_object_find_key(obj, "user");
62	if (!user || user->type != UCL_STRING) {
63		log_warnx("chap section in auth-group \"%s\" is missing "
64		    "\"user\" string key", auth_group->ag_name);
65		return (1);
66	}
67
68	secret = ucl_object_find_key(obj, "secret");
69	if (!secret || secret->type != UCL_STRING) {
70		log_warnx("chap section in auth-group \"%s\" is missing "
71		    "\"secret\" string key", auth_group->ag_name);
72	}
73
74	ca = auth_new_chap(auth_group,
75	    ucl_object_tostring(user),
76	    ucl_object_tostring(secret));
77
78	if (ca == NULL)
79		return (1);
80
81	return (0);
82}
83
84static int
85uclparse_chap_mutual(struct auth_group *auth_group, const ucl_object_t *obj)
86{
87	const struct auth *ca;
88	const ucl_object_t *user, *secret, *mutual_user;
89	const ucl_object_t *mutual_secret;
90
91	user = ucl_object_find_key(obj, "user");
92	if (!user || user->type != UCL_STRING) {
93		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
94		    "\"user\" string key", auth_group->ag_name);
95		return (1);
96	}
97
98	secret = ucl_object_find_key(obj, "secret");
99	if (!secret || secret->type != UCL_STRING) {
100		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
101		    "\"secret\" string key", auth_group->ag_name);
102		return (1);
103	}
104
105	mutual_user = ucl_object_find_key(obj, "mutual-user");
106	if (!user || user->type != UCL_STRING) {
107		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
108		    "\"mutual-user\" string key", auth_group->ag_name);
109		return (1);
110	}
111
112	mutual_secret = ucl_object_find_key(obj, "mutual-secret");
113	if (!secret || secret->type != UCL_STRING) {
114		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
115		    "\"mutual-secret\" string key", auth_group->ag_name);
116		return (1);
117	}
118
119	ca = auth_new_chap_mutual(auth_group,
120	    ucl_object_tostring(user),
121	    ucl_object_tostring(secret),
122	    ucl_object_tostring(mutual_user),
123	    ucl_object_tostring(mutual_secret));
124
125	if (ca == NULL)
126		return (1);
127
128	return (0);
129}
130
131static int
132uclparse_target_portal_group(struct target *target, const ucl_object_t *obj)
133{
134	struct portal_group *tpg;
135	struct auth_group *tag = NULL;
136	struct port *tp;
137	const ucl_object_t *portal_group, *auth_group;
138
139	portal_group = ucl_object_find_key(obj, "name");
140	if (!portal_group || portal_group->type != UCL_STRING) {
141		log_warnx("portal-group section in target \"%s\" is missing "
142		    "\"name\" string key", target->t_name);
143		return (1);
144	}
145
146	auth_group = ucl_object_find_key(obj, "auth-group-name");
147	if (auth_group && auth_group->type != UCL_STRING) {
148		log_warnx("portal-group section in target \"%s\" is missing "
149		    "\"auth-group-name\" string key", target->t_name);
150		return (1);
151	}
152
153
154	tpg = portal_group_find(conf, ucl_object_tostring(portal_group));
155	if (tpg == NULL) {
156		log_warnx("unknown portal-group \"%s\" for target "
157		    "\"%s\"", ucl_object_tostring(portal_group), target->t_name);
158		return (1);
159	}
160
161	if (auth_group) {
162		tag = auth_group_find(conf, ucl_object_tostring(auth_group));
163		if (tag == NULL) {
164			log_warnx("unknown auth-group \"%s\" for target "
165			    "\"%s\"", ucl_object_tostring(auth_group),
166			    target->t_name);
167			return (1);
168		}
169	}
170
171	tp = port_new(conf, target, tpg);
172	if (tp == NULL) {
173		log_warnx("can't link portal-group \"%s\" to target "
174		    "\"%s\"", ucl_object_tostring(portal_group), target->t_name);
175		return (1);
176	}
177	tp->p_auth_group = tag;
178
179	return (0);
180}
181
182static int
183uclparse_target_lun(struct target *target, const ucl_object_t *obj)
184{
185	struct lun *lun;
186
187	if (obj->type == UCL_INT) {
188		char *name;
189
190		asprintf(&name, "%s,lun,%ju", target->t_name,
191		    ucl_object_toint(obj));
192		lun = lun_new(conf, name);
193		if (lun == NULL)
194			return (1);
195
196		lun_set_scsiname(lun, name);
197		target->t_luns[ucl_object_toint(obj)] = lun;
198		return (0);
199	}
200
201	if (obj->type == UCL_OBJECT) {
202		const ucl_object_t *num = ucl_object_find_key(obj, "number");
203		const ucl_object_t *name = ucl_object_find_key(obj, "name");
204
205		if (num == NULL || num->type != UCL_INT) {
206			log_warnx("lun section in target \"%s\" is missing "
207			    "\"number\" integer property", target->t_name);
208			return (1);
209		}
210
211		if (name == NULL || name->type != UCL_STRING) {
212			log_warnx("lun section in target \"%s\" is missing "
213			    "\"name\" string property", target->t_name);
214			return (1);
215		}
216
217		lun = lun_find(conf, ucl_object_tostring(name));
218		if (lun == NULL)
219			return (1);
220
221		target->t_luns[ucl_object_toint(num)] = lun;
222	}
223
224	return (0);
225}
226
227static int
228uclparse_toplevel(const ucl_object_t *top)
229{
230	ucl_object_iter_t it = NULL, iter = NULL;
231	const ucl_object_t *obj = NULL, *child = NULL;
232	int err = 0;
233
234	/* Pass 1 - everything except targets */
235	while ((obj = ucl_iterate_object(top, &it, true))) {
236		const char *key = ucl_object_key(obj);
237
238		if (!strcmp(key, "debug")) {
239			if (obj->type == UCL_INT)
240				conf->conf_debug = ucl_object_toint(obj);
241			else {
242				log_warnx("\"debug\" property value is not integer");
243				return (1);
244			}
245		}
246
247		if (!strcmp(key, "timeout")) {
248			if (obj->type == UCL_INT)
249				conf->conf_timeout = ucl_object_toint(obj);
250			else {
251				log_warnx("\"timeout\" property value is not integer");
252				return (1);
253			}
254		}
255
256		if (!strcmp(key, "maxproc")) {
257			if (obj->type == UCL_INT)
258				conf->conf_maxproc = ucl_object_toint(obj);
259			else {
260				log_warnx("\"maxproc\" property value is not integer");
261				return (1);
262			}
263		}
264
265		if (!strcmp(key, "pidfile")) {
266			if (obj->type == UCL_STRING)
267				conf->conf_pidfile_path = strdup(
268				    ucl_object_tostring(obj));
269			else {
270				log_warnx("\"pidfile\" property value is not string");
271				return (1);
272			}
273		}
274
275		if (!strcmp(key, "isns-server")) {
276			if (obj->type == UCL_ARRAY) {
277				iter = NULL;
278				while ((child = ucl_iterate_object(obj, &iter,
279				    true))) {
280					if (child->type != UCL_STRING)
281						return (1);
282
283					err = isns_new(conf,
284					    ucl_object_tostring(child));
285					if (err != 0) {
286						return (1);
287					}
288				}
289			} else {
290				log_warnx("\"isns-server\" property value is "
291				    "not an array");
292				return (1);
293			}
294		}
295
296		if (!strcmp(key, "isns-period")) {
297			if (obj->type == UCL_INT)
298				conf->conf_timeout = ucl_object_toint(obj);
299			else {
300				log_warnx("\"isns-period\" property value is not integer");
301				return (1);
302			}
303		}
304
305		if (!strcmp(key, "isns-timeout")) {
306			if (obj->type == UCL_INT)
307				conf->conf_timeout = ucl_object_toint(obj);
308			else {
309				log_warnx("\"isns-timeout\" property value is not integer");
310				return (1);
311			}
312		}
313
314		if (!strcmp(key, "auth-group")) {
315			if (obj->type == UCL_OBJECT) {
316				iter = NULL;
317				while ((child = ucl_iterate_object(obj, &iter, true))) {
318					uclparse_auth_group(ucl_object_key(child), child);
319				}
320			} else {
321				log_warnx("\"auth-group\" section is not an object");
322				return (1);
323			}
324		}
325
326		if (!strcmp(key, "portal-group")) {
327			if (obj->type == UCL_OBJECT) {
328				iter = NULL;
329				while ((child = ucl_iterate_object(obj, &iter, true))) {
330					uclparse_portal_group(ucl_object_key(child), child);
331				}
332			} else {
333				log_warnx("\"portal-group\" section is not an object");
334				return (1);
335			}
336		}
337
338		if (!strcmp(key, "lun")) {
339			if (obj->type == UCL_OBJECT) {
340				iter = NULL;
341				while ((child = ucl_iterate_object(obj, &iter, true))) {
342					uclparse_lun(ucl_object_key(child), child);
343				}
344			} else {
345				log_warnx("\"lun\" section is not an object");
346				return (1);
347			}
348		}
349	}
350
351	/* Pass 2 - targets */
352	it = NULL;
353	while ((obj = ucl_iterate_object(top, &it, true))) {
354		const char *key = ucl_object_key(obj);
355
356		if (!strcmp(key, "target")) {
357			if (obj->type == UCL_OBJECT) {
358				iter = NULL;
359				while ((child = ucl_iterate_object(obj, &iter,
360				    true))) {
361					uclparse_target(ucl_object_key(child),
362					    child);
363				}
364			} else {
365				log_warnx("\"target\" section is not an object");
366				return (1);
367			}
368		}
369	}
370
371	return (0);
372}
373
374static int
375uclparse_auth_group(const char *name, const ucl_object_t *top)
376{
377	struct auth_group *auth_group;
378	const struct auth_name *an;
379	const struct auth_portal *ap;
380	ucl_object_iter_t it = NULL, it2 = NULL;
381	const ucl_object_t *obj = NULL, *tmp = NULL;
382	const char *key;
383	int err;
384
385	if (!strcmp(name, "default") &&
386	    conf->conf_default_ag_defined == false) {
387		auth_group = auth_group_find(conf, name);
388		conf->conf_default_ag_defined = true;
389	} else {
390		auth_group = auth_group_new(conf, name);
391	}
392
393	if (auth_group == NULL)
394		return (1);
395
396	while ((obj = ucl_iterate_object(top, &it, true))) {
397		key = ucl_object_key(obj);
398
399		if (!strcmp(key, "auth-type")) {
400			const char *value = ucl_object_tostring(obj);
401
402			err = auth_group_set_type(auth_group, value);
403			if (err)
404				return (1);
405		}
406
407		if (!strcmp(key, "chap")) {
408			if (obj->type != UCL_ARRAY) {
409				log_warnx("\"chap\" property of "
410				    "auth-group \"%s\" is not an array",
411				    name);
412				return (1);
413			}
414
415			it2 = NULL;
416			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
417				if (uclparse_chap(auth_group, tmp) != 0)
418					return (1);
419			}
420		}
421
422		if (!strcmp(key, "chap-mutual")) {
423			if (obj->type != UCL_ARRAY) {
424				log_warnx("\"chap-mutual\" property of "
425				    "auth-group \"%s\" is not an array",
426				    name);
427				return (1);
428			}
429
430			it2 = NULL;
431			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
432				if (uclparse_chap_mutual(auth_group, tmp) != 0)
433					return (1);
434			}
435		}
436
437		if (!strcmp(key, "initiator-name")) {
438			if (obj->type != UCL_ARRAY) {
439				log_warnx("\"initiator-name\" property of "
440				    "auth-group \"%s\" is not an array",
441				    name);
442				return (1);
443			}
444
445			it2 = NULL;
446			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
447				const char *value = ucl_object_tostring(tmp);
448
449				an = auth_name_new(auth_group, value);
450				if (an == NULL)
451					return (1);
452			}
453		}
454
455		if (!strcmp(key, "initiator-portal")) {
456			if (obj->type != UCL_ARRAY) {
457				log_warnx("\"initiator-portal\" property of "
458				    "auth-group \"%s\" is not an array",
459				    name);
460				return (1);
461			}
462
463			it2 = NULL;
464			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
465				const char *value = ucl_object_tostring(tmp);
466
467				ap = auth_portal_new(auth_group, value);
468				if (ap == NULL)
469					return (1);
470			}
471		}
472	}
473
474	return (0);
475}
476
477static int
478uclparse_portal_group(const char *name, const ucl_object_t *top)
479{
480	struct portal_group *portal_group;
481	ucl_object_iter_t it = NULL, it2 = NULL;
482	const ucl_object_t *obj = NULL, *tmp = NULL;
483	const char *key;
484
485	if (strcmp(name, "default") == 0 &&
486	    conf->conf_default_pg_defined == false) {
487		portal_group = portal_group_find(conf, name);
488		conf->conf_default_pg_defined = true;
489	} else {
490		portal_group = portal_group_new(conf, name);
491	}
492
493	if (portal_group == NULL)
494		return (1);
495
496	while ((obj = ucl_iterate_object(top, &it, true))) {
497		key = ucl_object_key(obj);
498
499		if (!strcmp(key, "discovery-auth-group")) {
500			portal_group->pg_discovery_auth_group =
501			    auth_group_find(conf, ucl_object_tostring(obj));
502			if (portal_group->pg_discovery_auth_group == NULL) {
503				log_warnx("unknown discovery-auth-group \"%s\" "
504				    "for portal-group \"%s\"",
505				    ucl_object_tostring(obj),
506				    portal_group->pg_name);
507				return (1);
508			}
509		}
510
511		if (!strcmp(key, "discovery-filter")) {
512			if (obj->type != UCL_STRING) {
513				log_warnx("\"discovery-filter\" property of "
514				    "portal-group \"%s\" is not a string",
515				    portal_group->pg_name);
516				return (1);
517			}
518
519			if (portal_group_set_filter(portal_group,
520			    ucl_object_tostring(obj)) != 0)
521				return (1);
522		}
523
524		if (!strcmp(key, "listen")) {
525			if (obj->type == UCL_STRING) {
526				if (portal_group_add_listen(portal_group,
527				    ucl_object_tostring(obj), false) != 0)
528					return (1);
529			} else if (obj->type == UCL_ARRAY) {
530				while ((tmp = ucl_iterate_object(obj, &it2,
531				    true))) {
532					if (portal_group_add_listen(
533					    portal_group,
534					    ucl_object_tostring(tmp),
535					    false) != 0)
536						return (1);
537				}
538			} else {
539				log_warnx("\"listen\" property of "
540				    "portal-group \"%s\" is not a string",
541				    portal_group->pg_name);
542				return (1);
543			}
544		}
545
546		if (!strcmp(key, "listen-iser")) {
547			if (obj->type == UCL_STRING) {
548				if (portal_group_add_listen(portal_group,
549				    ucl_object_tostring(obj), true) != 0)
550					return (1);
551			} else if (obj->type == UCL_ARRAY) {
552				while ((tmp = ucl_iterate_object(obj, &it2,
553				    true))) {
554					if (portal_group_add_listen(
555					    portal_group,
556					    ucl_object_tostring(tmp),
557					    true) != 0)
558						return (1);
559				}
560			} else {
561				log_warnx("\"listen\" property of "
562				    "portal-group \"%s\" is not a string",
563				    portal_group->pg_name);
564				return (1);
565			}
566		}
567
568		if (!strcmp(key, "redirect")) {
569			if (obj->type != UCL_STRING) {
570				log_warnx("\"listen\" property of "
571				    "portal-group \"%s\" is not a string",
572				    portal_group->pg_name);
573				return (1);
574			}
575
576			if (portal_group_set_redirection(portal_group,
577			    ucl_object_tostring(obj)) != 0)
578				return (1);
579		}
580
581		if (!strcmp(key, "options")) {
582			if (obj->type != UCL_OBJECT) {
583				log_warnx("\"options\" property of portal group "
584				    "\"%s\" is not an object", portal_group->pg_name);
585				return (1);
586			}
587
588			while ((tmp = ucl_iterate_object(obj, &it2,
589			    true))) {
590				option_new(&portal_group->pg_options,
591				    ucl_object_key(tmp),
592				    ucl_object_tostring_forced(tmp));
593			}
594		}
595	}
596
597	return (0);
598}
599
600static int
601uclparse_target(const char *name, const ucl_object_t *top)
602{
603	struct target *target;
604	ucl_object_iter_t it = NULL, it2 = NULL;
605	const ucl_object_t *obj = NULL, *tmp = NULL;
606	const char *key;
607
608	target = target_new(conf, name);
609
610	while ((obj = ucl_iterate_object(top, &it, true))) {
611		key = ucl_object_key(obj);
612
613		if (!strcmp(key, "alias")) {
614			if (obj->type != UCL_STRING) {
615				log_warnx("\"alias\" property of target "
616				    "\"%s\" is not a string", target->t_name);
617				return (1);
618			}
619
620			target->t_alias = strdup(ucl_object_tostring(obj));
621		}
622
623		if (!strcmp(key, "auth-group")) {
624			if (target->t_auth_group != NULL) {
625				if (target->t_auth_group->ag_name != NULL)
626					log_warnx("auth-group for target \"%s\" "
627					    "specified more than once",
628					    target->t_name);
629				else
630					log_warnx("cannot use both auth-group "
631					    "and explicit authorisations for "
632					    "target \"%s\"", target->t_name);
633				return (1);
634			}
635			target->t_auth_group = auth_group_find(conf,
636			    ucl_object_tostring(obj));
637			if (target->t_auth_group == NULL) {
638				log_warnx("unknown auth-group \"%s\" for target "
639				    "\"%s\"", ucl_object_tostring(obj),
640				    target->t_name);
641				return (1);
642			}
643		}
644
645		if (!strcmp(key, "auth-type")) {
646			int error;
647
648			if (target->t_auth_group != NULL) {
649				if (target->t_auth_group->ag_name != NULL) {
650					log_warnx("cannot use both auth-group and "
651					    "auth-type for target \"%s\"",
652					    target->t_name);
653					return (1);
654				}
655			} else {
656				target->t_auth_group = auth_group_new(conf, NULL);
657				if (target->t_auth_group == NULL)
658					return (1);
659
660				target->t_auth_group->ag_target = target;
661			}
662			error = auth_group_set_type(target->t_auth_group,
663			    ucl_object_tostring(obj));
664			if (error != 0)
665				return (1);
666		}
667
668		if (!strcmp(key, "chap")) {
669			if (uclparse_chap(target->t_auth_group, obj) != 0)
670				return (1);
671		}
672
673		if (!strcmp(key, "chap-mutual")) {
674			if (uclparse_chap_mutual(target->t_auth_group, obj) != 0)
675				return (1);
676		}
677
678		if (!strcmp(key, "initiator-name")) {
679			const struct auth_name *an;
680
681			if (target->t_auth_group != NULL) {
682				if (target->t_auth_group->ag_name != NULL) {
683					log_warnx("cannot use both auth-group and "
684					    "initiator-name for target \"%s\"",
685					    target->t_name);
686					return (1);
687				}
688			} else {
689				target->t_auth_group = auth_group_new(conf, NULL);
690				if (target->t_auth_group == NULL)
691					return (1);
692
693				target->t_auth_group->ag_target = target;
694			}
695			an = auth_name_new(target->t_auth_group,
696			    ucl_object_tostring(obj));
697			if (an == NULL)
698				return (1);
699		}
700
701		if (!strcmp(key, "initiator-portal")) {
702			const struct auth_portal *ap;
703
704			if (target->t_auth_group != NULL) {
705				if (target->t_auth_group->ag_name != NULL) {
706					log_warnx("cannot use both auth-group and "
707					    "initiator-portal for target \"%s\"",
708					    target->t_name);
709					return (1);
710				}
711			} else {
712				target->t_auth_group = auth_group_new(conf, NULL);
713				if (target->t_auth_group == NULL)
714					return (1);
715
716				target->t_auth_group->ag_target = target;
717			}
718			ap = auth_portal_new(target->t_auth_group,
719			    ucl_object_tostring(obj));
720			if (ap == NULL)
721				return (1);
722		}
723
724		if (!strcmp(key, "portal-group")) {
725			if (obj->type == UCL_OBJECT) {
726				if (uclparse_target_portal_group(target, obj) != 0)
727					return (1);
728			}
729
730			if (obj->type == UCL_ARRAY) {
731				while ((tmp = ucl_iterate_object(obj, &it2,
732				    true))) {
733					if (uclparse_target_portal_group(target,
734					    tmp) != 0)
735						return (1);
736				}
737			}
738		}
739
740		if (!strcmp(key, "port")) {
741			struct pport *pp;
742			struct port *tp;
743			const char *value = ucl_object_tostring(obj);
744
745			pp = pport_find(conf, value);
746			if (pp == NULL) {
747				log_warnx("unknown port \"%s\" for target \"%s\"",
748				    value, target->t_name);
749				return (1);
750			}
751			if (!TAILQ_EMPTY(&pp->pp_ports)) {
752				log_warnx("can't link port \"%s\" to target \"%s\", "
753				    "port already linked to some target",
754				    value, target->t_name);
755				return (1);
756			}
757			tp = port_new_pp(conf, target, pp);
758			if (tp == NULL) {
759				log_warnx("can't link port \"%s\" to target \"%s\"",
760				    value, target->t_name);
761				return (1);
762			}
763		}
764
765		if (!strcmp(key, "redirect")) {
766			if (obj->type != UCL_STRING) {
767				log_warnx("\"redirect\" property of target "
768				    "\"%s\" is not a string", target->t_name);
769				return (1);
770			}
771
772			if (target_set_redirection(target,
773			    ucl_object_tostring(obj)) != 0)
774				return (1);
775		}
776
777		if (!strcmp(key, "lun")) {
778			while ((tmp = ucl_iterate_object(obj, &it2, true))) {
779				if (uclparse_target_lun(target, tmp) != 0)
780					return (1);
781			}
782		}
783	}
784
785	return (0);
786}
787
788static int
789uclparse_lun(const char *name, const ucl_object_t *top)
790{
791	struct lun *lun;
792	ucl_object_iter_t it = NULL, child_it = NULL;
793	const ucl_object_t *obj = NULL, *child = NULL;
794	const char *key;
795
796	lun = lun_new(conf, name);
797
798	while ((obj = ucl_iterate_object(top, &it, true))) {
799		key = ucl_object_key(obj);
800
801		if (!strcmp(key, "backend")) {
802			if (obj->type != UCL_STRING) {
803				log_warnx("\"backend\" property of lun "
804				    "\"%s\" is not a string",
805				    lun->l_name);
806				return (1);
807			}
808
809			lun_set_backend(lun, ucl_object_tostring(obj));
810		}
811
812		if (!strcmp(key, "blocksize")) {
813			if (obj->type != UCL_INT) {
814				log_warnx("\"blocksize\" property of lun "
815				    "\"%s\" is not an integer", lun->l_name);
816				return (1);
817			}
818
819			lun_set_blocksize(lun, ucl_object_toint(obj));
820		}
821
822		if (!strcmp(key, "device-id")) {
823			if (obj->type != UCL_STRING) {
824				log_warnx("\"device-id\" property of lun "
825				    "\"%s\" is not an integer", lun->l_name);
826				return (1);
827			}
828
829			lun_set_device_id(lun, ucl_object_tostring(obj));
830		}
831
832		if (!strcmp(key, "options")) {
833			if (obj->type != UCL_OBJECT) {
834				log_warnx("\"options\" property of lun "
835				    "\"%s\" is not an object", lun->l_name);
836				return (1);
837			}
838
839			while ((child = ucl_iterate_object(obj, &child_it,
840			    true))) {
841				option_new(&lun->l_options,
842				    ucl_object_key(child),
843				    ucl_object_tostring_forced(child));
844			}
845		}
846
847		if (!strcmp(key, "path")) {
848			if (obj->type != UCL_STRING) {
849				log_warnx("\"path\" property of lun "
850				    "\"%s\" is not a string", lun->l_name);
851				return (1);
852			}
853
854			lun_set_path(lun, ucl_object_tostring(obj));
855		}
856
857		if (!strcmp(key, "serial")) {
858			if (obj->type != UCL_STRING) {
859				log_warnx("\"serial\" property of lun "
860				    "\"%s\" is not a string", lun->l_name);
861				return (1);
862			}
863
864			lun_set_serial(lun, ucl_object_tostring(obj));
865		}
866
867		if (!strcmp(key, "size")) {
868			if (obj->type != UCL_INT) {
869				log_warnx("\"size\" property of lun "
870				    "\"%s\" is not an integer", lun->l_name);
871				return (1);
872			}
873
874			lun_set_size(lun, ucl_object_toint(obj));
875		}
876	}
877
878	return (0);
879}
880
881int
882uclparse_conf(struct conf *newconf, const char *path)
883{
884	struct ucl_parser *parser;
885	int error;
886
887	conf = newconf;
888	parser = ucl_parser_new(0);
889
890	if (!ucl_parser_add_file(parser, path)) {
891		log_warn("unable to parse configuration file %s: %s", path,
892		    ucl_parser_get_error(parser));
893		return (1);
894	}
895
896	error = uclparse_toplevel(ucl_parser_get_object(parser));
897
898	return (error);
899}
900