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