xref: /freebsd/usr.sbin/ctld/parse.y (revision 4f52dfbb)
1 %{
2 /*-
3  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4  *
5  * Copyright (c) 2012 The FreeBSD Foundation
6  * All rights reserved.
7  *
8  * This software was developed by Edward Tomasz Napierala under sponsorship
9  * from the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD$
33  */
34 
35 #include <sys/queue.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <assert.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 #include "ctld.h"
44 
45 extern FILE *yyin;
46 extern char *yytext;
47 extern int lineno;
48 
49 static struct conf *conf = NULL;
50 static struct auth_group *auth_group = NULL;
51 static struct portal_group *portal_group = NULL;
52 static struct target *target = NULL;
53 static struct lun *lun = NULL;
54 
55 extern void	yyerror(const char *);
56 extern int	yylex(void);
57 extern void	yyrestart(FILE *);
58 
59 %}
60 
61 %token ALIAS AUTH_GROUP AUTH_TYPE BACKEND BLOCKSIZE CHAP CHAP_MUTUAL
62 %token CLOSING_BRACKET CTL_LUN DEBUG DEVICE_ID DEVICE_TYPE
63 %token DISCOVERY_AUTH_GROUP DISCOVERY_FILTER FOREIGN
64 %token INITIATOR_NAME INITIATOR_PORTAL ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT
65 %token LISTEN LISTEN_ISER LUN MAXPROC OFFLOAD OPENING_BRACKET OPTION
66 %token PATH PIDFILE PORT PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR
67 %token TAG TARGET TIMEOUT
68 
69 %union
70 {
71 	char *str;
72 }
73 
74 %token <str> STR
75 
76 %%
77 
78 statements:
79 	|
80 	statements statement
81 	|
82 	statements statement SEMICOLON
83 	;
84 
85 statement:
86 	debug
87 	|
88 	timeout
89 	|
90 	maxproc
91 	|
92 	pidfile
93 	|
94 	isns_server
95 	|
96 	isns_period
97 	|
98 	isns_timeout
99 	|
100 	auth_group
101 	|
102 	portal_group
103 	|
104 	lun
105 	|
106 	target
107 	;
108 
109 debug:		DEBUG STR
110 	{
111 		uint64_t tmp;
112 
113 		if (expand_number($2, &tmp) != 0) {
114 			yyerror("invalid numeric value");
115 			free($2);
116 			return (1);
117 		}
118 
119 		conf->conf_debug = tmp;
120 	}
121 	;
122 
123 timeout:	TIMEOUT STR
124 	{
125 		uint64_t tmp;
126 
127 		if (expand_number($2, &tmp) != 0) {
128 			yyerror("invalid numeric value");
129 			free($2);
130 			return (1);
131 		}
132 
133 		conf->conf_timeout = tmp;
134 	}
135 	;
136 
137 maxproc:	MAXPROC STR
138 	{
139 		uint64_t tmp;
140 
141 		if (expand_number($2, &tmp) != 0) {
142 			yyerror("invalid numeric value");
143 			free($2);
144 			return (1);
145 		}
146 
147 		conf->conf_maxproc = tmp;
148 	}
149 	;
150 
151 pidfile:	PIDFILE STR
152 	{
153 		if (conf->conf_pidfile_path != NULL) {
154 			log_warnx("pidfile specified more than once");
155 			free($2);
156 			return (1);
157 		}
158 		conf->conf_pidfile_path = $2;
159 	}
160 	;
161 
162 isns_server:	ISNS_SERVER STR
163 	{
164 		int error;
165 
166 		error = isns_new(conf, $2);
167 		free($2);
168 		if (error != 0)
169 			return (1);
170 	}
171 	;
172 
173 isns_period:	ISNS_PERIOD STR
174 	{
175 		uint64_t tmp;
176 
177 		if (expand_number($2, &tmp) != 0) {
178 			yyerror("invalid numeric value");
179 			free($2);
180 			return (1);
181 		}
182 
183 		conf->conf_isns_period = tmp;
184 	}
185 	;
186 
187 isns_timeout:	ISNS_TIMEOUT STR
188 	{
189 		uint64_t tmp;
190 
191 		if (expand_number($2, &tmp) != 0) {
192 			yyerror("invalid numeric value");
193 			free($2);
194 			return (1);
195 		}
196 
197 		conf->conf_isns_timeout = tmp;
198 	}
199 	;
200 
201 auth_group:	AUTH_GROUP auth_group_name
202     OPENING_BRACKET auth_group_entries CLOSING_BRACKET
203 	{
204 		auth_group = NULL;
205 	}
206 	;
207 
208 auth_group_name:	STR
209 	{
210 		/*
211 		 * Make it possible to redefine default
212 		 * auth-group. but only once.
213 		 */
214 		if (strcmp($1, "default") == 0 &&
215 		    conf->conf_default_ag_defined == false) {
216 			auth_group = auth_group_find(conf, $1);
217 			conf->conf_default_ag_defined = true;
218 		} else {
219 			auth_group = auth_group_new(conf, $1);
220 		}
221 		free($1);
222 		if (auth_group == NULL)
223 			return (1);
224 	}
225 	;
226 
227 auth_group_entries:
228 	|
229 	auth_group_entries auth_group_entry
230 	|
231 	auth_group_entries auth_group_entry SEMICOLON
232 	;
233 
234 auth_group_entry:
235 	auth_group_auth_type
236 	|
237 	auth_group_chap
238 	|
239 	auth_group_chap_mutual
240 	|
241 	auth_group_initiator_name
242 	|
243 	auth_group_initiator_portal
244 	;
245 
246 auth_group_auth_type:	AUTH_TYPE STR
247 	{
248 		int error;
249 
250 		error = auth_group_set_type(auth_group, $2);
251 		free($2);
252 		if (error != 0)
253 			return (1);
254 	}
255 	;
256 
257 auth_group_chap:	CHAP STR STR
258 	{
259 		const struct auth *ca;
260 
261 		ca = auth_new_chap(auth_group, $2, $3);
262 		free($2);
263 		free($3);
264 		if (ca == NULL)
265 			return (1);
266 	}
267 	;
268 
269 auth_group_chap_mutual:	CHAP_MUTUAL STR STR STR STR
270 	{
271 		const struct auth *ca;
272 
273 		ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5);
274 		free($2);
275 		free($3);
276 		free($4);
277 		free($5);
278 		if (ca == NULL)
279 			return (1);
280 	}
281 	;
282 
283 auth_group_initiator_name:	INITIATOR_NAME STR
284 	{
285 		const struct auth_name *an;
286 
287 		an = auth_name_new(auth_group, $2);
288 		free($2);
289 		if (an == NULL)
290 			return (1);
291 	}
292 	;
293 
294 auth_group_initiator_portal:	INITIATOR_PORTAL STR
295 	{
296 		const struct auth_portal *ap;
297 
298 		ap = auth_portal_new(auth_group, $2);
299 		free($2);
300 		if (ap == NULL)
301 			return (1);
302 	}
303 	;
304 
305 portal_group:	PORTAL_GROUP portal_group_name
306     OPENING_BRACKET portal_group_entries CLOSING_BRACKET
307 	{
308 		portal_group = NULL;
309 	}
310 	;
311 
312 portal_group_name:	STR
313 	{
314 		/*
315 		 * Make it possible to redefine default
316 		 * portal-group. but only once.
317 		 */
318 		if (strcmp($1, "default") == 0 &&
319 		    conf->conf_default_pg_defined == false) {
320 			portal_group = portal_group_find(conf, $1);
321 			conf->conf_default_pg_defined = true;
322 		} else {
323 			portal_group = portal_group_new(conf, $1);
324 		}
325 		free($1);
326 		if (portal_group == NULL)
327 			return (1);
328 	}
329 	;
330 
331 portal_group_entries:
332 	|
333 	portal_group_entries portal_group_entry
334 	|
335 	portal_group_entries portal_group_entry SEMICOLON
336 	;
337 
338 portal_group_entry:
339 	portal_group_discovery_auth_group
340 	|
341 	portal_group_discovery_filter
342 	|
343 	portal_group_foreign
344 	|
345 	portal_group_listen
346 	|
347 	portal_group_listen_iser
348 	|
349 	portal_group_offload
350 	|
351 	portal_group_option
352 	|
353 	portal_group_redirect
354 	|
355 	portal_group_tag
356 	;
357 
358 portal_group_discovery_auth_group:	DISCOVERY_AUTH_GROUP STR
359 	{
360 		if (portal_group->pg_discovery_auth_group != NULL) {
361 			log_warnx("discovery-auth-group for portal-group "
362 			    "\"%s\" specified more than once",
363 			    portal_group->pg_name);
364 			return (1);
365 		}
366 		portal_group->pg_discovery_auth_group =
367 		    auth_group_find(conf, $2);
368 		if (portal_group->pg_discovery_auth_group == NULL) {
369 			log_warnx("unknown discovery-auth-group \"%s\" "
370 			    "for portal-group \"%s\"",
371 			    $2, portal_group->pg_name);
372 			return (1);
373 		}
374 		free($2);
375 	}
376 	;
377 
378 portal_group_discovery_filter:	DISCOVERY_FILTER STR
379 	{
380 		int error;
381 
382 		error = portal_group_set_filter(portal_group, $2);
383 		free($2);
384 		if (error != 0)
385 			return (1);
386 	}
387 	;
388 
389 portal_group_foreign:	FOREIGN
390 	{
391 
392 		portal_group->pg_foreign = 1;
393 	}
394 	;
395 
396 portal_group_listen:	LISTEN STR
397 	{
398 		int error;
399 
400 		error = portal_group_add_listen(portal_group, $2, false);
401 		free($2);
402 		if (error != 0)
403 			return (1);
404 	}
405 	;
406 
407 portal_group_listen_iser:	LISTEN_ISER STR
408 	{
409 		int error;
410 
411 		error = portal_group_add_listen(portal_group, $2, true);
412 		free($2);
413 		if (error != 0)
414 			return (1);
415 	}
416 	;
417 
418 portal_group_offload:	OFFLOAD STR
419 	{
420 		int error;
421 
422 		error = portal_group_set_offload(portal_group, $2);
423 		free($2);
424 		if (error != 0)
425 			return (1);
426 	}
427 	;
428 
429 portal_group_option:	OPTION STR STR
430 	{
431 		struct option *o;
432 
433 		o = option_new(&portal_group->pg_options, $2, $3);
434 		free($2);
435 		free($3);
436 		if (o == NULL)
437 			return (1);
438 	}
439 	;
440 
441 portal_group_redirect:	REDIRECT STR
442 	{
443 		int error;
444 
445 		error = portal_group_set_redirection(portal_group, $2);
446 		free($2);
447 		if (error != 0)
448 			return (1);
449 	}
450 	;
451 
452 portal_group_tag:	TAG STR
453 	{
454 		uint64_t tmp;
455 
456 		if (expand_number($2, &tmp) != 0) {
457 			yyerror("invalid numeric value");
458 			free($2);
459 			return (1);
460 		}
461 
462 		portal_group->pg_tag = tmp;
463 	}
464 	;
465 
466 lun:	LUN lun_name
467     OPENING_BRACKET lun_entries CLOSING_BRACKET
468 	{
469 		lun = NULL;
470 	}
471 	;
472 
473 lun_name:	STR
474 	{
475 		lun = lun_new(conf, $1);
476 		free($1);
477 		if (lun == NULL)
478 			return (1);
479 	}
480 	;
481 
482 target:	TARGET target_name
483     OPENING_BRACKET target_entries CLOSING_BRACKET
484 	{
485 		target = NULL;
486 	}
487 	;
488 
489 target_name:	STR
490 	{
491 		target = target_new(conf, $1);
492 		free($1);
493 		if (target == NULL)
494 			return (1);
495 	}
496 	;
497 
498 target_entries:
499 	|
500 	target_entries target_entry
501 	|
502 	target_entries target_entry SEMICOLON
503 	;
504 
505 target_entry:
506 	target_alias
507 	|
508 	target_auth_group
509 	|
510 	target_auth_type
511 	|
512 	target_chap
513 	|
514 	target_chap_mutual
515 	|
516 	target_initiator_name
517 	|
518 	target_initiator_portal
519 	|
520 	target_portal_group
521 	|
522 	target_port
523 	|
524 	target_redirect
525 	|
526 	target_lun
527 	|
528 	target_lun_ref
529 	;
530 
531 target_alias:	ALIAS STR
532 	{
533 		if (target->t_alias != NULL) {
534 			log_warnx("alias for target \"%s\" "
535 			    "specified more than once", target->t_name);
536 			return (1);
537 		}
538 		target->t_alias = $2;
539 	}
540 	;
541 
542 target_auth_group:	AUTH_GROUP STR
543 	{
544 		if (target->t_auth_group != NULL) {
545 			if (target->t_auth_group->ag_name != NULL)
546 				log_warnx("auth-group for target \"%s\" "
547 				    "specified more than once", target->t_name);
548 			else
549 				log_warnx("cannot use both auth-group and explicit "
550 				    "authorisations for target \"%s\"",
551 				    target->t_name);
552 			return (1);
553 		}
554 		target->t_auth_group = auth_group_find(conf, $2);
555 		if (target->t_auth_group == NULL) {
556 			log_warnx("unknown auth-group \"%s\" for target "
557 			    "\"%s\"", $2, target->t_name);
558 			return (1);
559 		}
560 		free($2);
561 	}
562 	;
563 
564 target_auth_type:	AUTH_TYPE STR
565 	{
566 		int error;
567 
568 		if (target->t_auth_group != NULL) {
569 			if (target->t_auth_group->ag_name != NULL) {
570 				log_warnx("cannot use both auth-group and "
571 				    "auth-type for target \"%s\"",
572 				    target->t_name);
573 				return (1);
574 			}
575 		} else {
576 			target->t_auth_group = auth_group_new(conf, NULL);
577 			if (target->t_auth_group == NULL) {
578 				free($2);
579 				return (1);
580 			}
581 			target->t_auth_group->ag_target = target;
582 		}
583 		error = auth_group_set_type(target->t_auth_group, $2);
584 		free($2);
585 		if (error != 0)
586 			return (1);
587 	}
588 	;
589 
590 target_chap:	CHAP STR STR
591 	{
592 		const struct auth *ca;
593 
594 		if (target->t_auth_group != NULL) {
595 			if (target->t_auth_group->ag_name != NULL) {
596 				log_warnx("cannot use both auth-group and "
597 				    "chap for target \"%s\"",
598 				    target->t_name);
599 				free($2);
600 				free($3);
601 				return (1);
602 			}
603 		} else {
604 			target->t_auth_group = auth_group_new(conf, NULL);
605 			if (target->t_auth_group == NULL) {
606 				free($2);
607 				free($3);
608 				return (1);
609 			}
610 			target->t_auth_group->ag_target = target;
611 		}
612 		ca = auth_new_chap(target->t_auth_group, $2, $3);
613 		free($2);
614 		free($3);
615 		if (ca == NULL)
616 			return (1);
617 	}
618 	;
619 
620 target_chap_mutual:	CHAP_MUTUAL STR STR STR STR
621 	{
622 		const struct auth *ca;
623 
624 		if (target->t_auth_group != NULL) {
625 			if (target->t_auth_group->ag_name != NULL) {
626 				log_warnx("cannot use both auth-group and "
627 				    "chap-mutual for target \"%s\"",
628 				    target->t_name);
629 				free($2);
630 				free($3);
631 				free($4);
632 				free($5);
633 				return (1);
634 			}
635 		} else {
636 			target->t_auth_group = auth_group_new(conf, NULL);
637 			if (target->t_auth_group == NULL) {
638 				free($2);
639 				free($3);
640 				free($4);
641 				free($5);
642 				return (1);
643 			}
644 			target->t_auth_group->ag_target = target;
645 		}
646 		ca = auth_new_chap_mutual(target->t_auth_group,
647 		    $2, $3, $4, $5);
648 		free($2);
649 		free($3);
650 		free($4);
651 		free($5);
652 		if (ca == NULL)
653 			return (1);
654 	}
655 	;
656 
657 target_initiator_name:	INITIATOR_NAME STR
658 	{
659 		const struct auth_name *an;
660 
661 		if (target->t_auth_group != NULL) {
662 			if (target->t_auth_group->ag_name != NULL) {
663 				log_warnx("cannot use both auth-group and "
664 				    "initiator-name for target \"%s\"",
665 				    target->t_name);
666 				free($2);
667 				return (1);
668 			}
669 		} else {
670 			target->t_auth_group = auth_group_new(conf, NULL);
671 			if (target->t_auth_group == NULL) {
672 				free($2);
673 				return (1);
674 			}
675 			target->t_auth_group->ag_target = target;
676 		}
677 		an = auth_name_new(target->t_auth_group, $2);
678 		free($2);
679 		if (an == NULL)
680 			return (1);
681 	}
682 	;
683 
684 target_initiator_portal:	INITIATOR_PORTAL STR
685 	{
686 		const struct auth_portal *ap;
687 
688 		if (target->t_auth_group != NULL) {
689 			if (target->t_auth_group->ag_name != NULL) {
690 				log_warnx("cannot use both auth-group and "
691 				    "initiator-portal for target \"%s\"",
692 				    target->t_name);
693 				free($2);
694 				return (1);
695 			}
696 		} else {
697 			target->t_auth_group = auth_group_new(conf, NULL);
698 			if (target->t_auth_group == NULL) {
699 				free($2);
700 				return (1);
701 			}
702 			target->t_auth_group->ag_target = target;
703 		}
704 		ap = auth_portal_new(target->t_auth_group, $2);
705 		free($2);
706 		if (ap == NULL)
707 			return (1);
708 	}
709 	;
710 
711 target_portal_group:	PORTAL_GROUP STR STR
712 	{
713 		struct portal_group *tpg;
714 		struct auth_group *tag;
715 		struct port *tp;
716 
717 		tpg = portal_group_find(conf, $2);
718 		if (tpg == NULL) {
719 			log_warnx("unknown portal-group \"%s\" for target "
720 			    "\"%s\"", $2, target->t_name);
721 			free($2);
722 			free($3);
723 			return (1);
724 		}
725 		tag = auth_group_find(conf, $3);
726 		if (tag == NULL) {
727 			log_warnx("unknown auth-group \"%s\" for target "
728 			    "\"%s\"", $3, target->t_name);
729 			free($2);
730 			free($3);
731 			return (1);
732 		}
733 		tp = port_new(conf, target, tpg);
734 		if (tp == NULL) {
735 			log_warnx("can't link portal-group \"%s\" to target "
736 			    "\"%s\"", $2, target->t_name);
737 			free($2);
738 			return (1);
739 		}
740 		tp->p_auth_group = tag;
741 		free($2);
742 		free($3);
743 	}
744 	|		PORTAL_GROUP STR
745 	{
746 		struct portal_group *tpg;
747 		struct port *tp;
748 
749 		tpg = portal_group_find(conf, $2);
750 		if (tpg == NULL) {
751 			log_warnx("unknown portal-group \"%s\" for target "
752 			    "\"%s\"", $2, target->t_name);
753 			free($2);
754 			return (1);
755 		}
756 		tp = port_new(conf, target, tpg);
757 		if (tp == NULL) {
758 			log_warnx("can't link portal-group \"%s\" to target "
759 			    "\"%s\"", $2, target->t_name);
760 			free($2);
761 			return (1);
762 		}
763 		free($2);
764 	}
765 	;
766 
767 target_port:	PORT STR
768 	{
769 		struct pport *pp;
770 		struct port *tp;
771 
772 		pp = pport_find(conf, $2);
773 		if (pp == NULL) {
774 			log_warnx("unknown port \"%s\" for target \"%s\"",
775 			    $2, target->t_name);
776 			free($2);
777 			return (1);
778 		}
779 		if (!TAILQ_EMPTY(&pp->pp_ports)) {
780 			log_warnx("can't link port \"%s\" to target \"%s\", "
781 			    "port already linked to some target",
782 			    $2, target->t_name);
783 			free($2);
784 			return (1);
785 		}
786 		tp = port_new_pp(conf, target, pp);
787 		if (tp == NULL) {
788 			log_warnx("can't link port \"%s\" to target \"%s\"",
789 			    $2, target->t_name);
790 			free($2);
791 			return (1);
792 		}
793 		free($2);
794 	}
795 	;
796 
797 target_redirect:	REDIRECT STR
798 	{
799 		int error;
800 
801 		error = target_set_redirection(target, $2);
802 		free($2);
803 		if (error != 0)
804 			return (1);
805 	}
806 	;
807 
808 target_lun:	LUN lun_number
809     OPENING_BRACKET lun_entries CLOSING_BRACKET
810 	{
811 		lun = NULL;
812 	}
813 	;
814 
815 lun_number:	STR
816 	{
817 		uint64_t tmp;
818 		int ret;
819 		char *name;
820 
821 		if (expand_number($1, &tmp) != 0) {
822 			yyerror("invalid numeric value");
823 			free($1);
824 			return (1);
825 		}
826 		if (tmp >= MAX_LUNS) {
827 			yyerror("LU number is too big");
828 			free($1);
829 			return (1);
830 		}
831 
832 		ret = asprintf(&name, "%s,lun,%ju", target->t_name, tmp);
833 		if (ret <= 0)
834 			log_err(1, "asprintf");
835 		lun = lun_new(conf, name);
836 		if (lun == NULL)
837 			return (1);
838 
839 		lun_set_scsiname(lun, name);
840 		target->t_luns[tmp] = lun;
841 	}
842 	;
843 
844 target_lun_ref:	LUN STR STR
845 	{
846 		uint64_t tmp;
847 
848 		if (expand_number($2, &tmp) != 0) {
849 			yyerror("invalid numeric value");
850 			free($2);
851 			free($3);
852 			return (1);
853 		}
854 		free($2);
855 		if (tmp >= MAX_LUNS) {
856 			yyerror("LU number is too big");
857 			free($3);
858 			return (1);
859 		}
860 
861 		lun = lun_find(conf, $3);
862 		free($3);
863 		if (lun == NULL)
864 			return (1);
865 
866 		target->t_luns[tmp] = lun;
867 	}
868 	;
869 
870 lun_entries:
871 	|
872 	lun_entries lun_entry
873 	|
874 	lun_entries lun_entry SEMICOLON
875 	;
876 
877 lun_entry:
878 	lun_backend
879 	|
880 	lun_blocksize
881 	|
882 	lun_device_id
883 	|
884 	lun_device_type
885 	|
886 	lun_ctl_lun
887 	|
888 	lun_option
889 	|
890 	lun_path
891 	|
892 	lun_serial
893 	|
894 	lun_size
895 	;
896 
897 lun_backend:	BACKEND STR
898 	{
899 		if (lun->l_backend != NULL) {
900 			log_warnx("backend for lun \"%s\" "
901 			    "specified more than once",
902 			    lun->l_name);
903 			free($2);
904 			return (1);
905 		}
906 		lun_set_backend(lun, $2);
907 		free($2);
908 	}
909 	;
910 
911 lun_blocksize:	BLOCKSIZE STR
912 	{
913 		uint64_t tmp;
914 
915 		if (expand_number($2, &tmp) != 0) {
916 			yyerror("invalid numeric value");
917 			free($2);
918 			return (1);
919 		}
920 
921 		if (lun->l_blocksize != 0) {
922 			log_warnx("blocksize for lun \"%s\" "
923 			    "specified more than once",
924 			    lun->l_name);
925 			return (1);
926 		}
927 		lun_set_blocksize(lun, tmp);
928 	}
929 	;
930 
931 lun_device_id:	DEVICE_ID STR
932 	{
933 		if (lun->l_device_id != NULL) {
934 			log_warnx("device_id for lun \"%s\" "
935 			    "specified more than once",
936 			    lun->l_name);
937 			free($2);
938 			return (1);
939 		}
940 		lun_set_device_id(lun, $2);
941 		free($2);
942 	}
943 	;
944 
945 lun_device_type:	DEVICE_TYPE STR
946 	{
947 		uint64_t tmp;
948 
949 		if (strcasecmp($2, "disk") == 0 ||
950 		    strcasecmp($2, "direct") == 0)
951 			tmp = 0;
952 		else if (strcasecmp($2, "processor") == 0)
953 			tmp = 3;
954 		else if (strcasecmp($2, "cd") == 0 ||
955 		    strcasecmp($2, "cdrom") == 0 ||
956 		    strcasecmp($2, "dvd") == 0 ||
957 		    strcasecmp($2, "dvdrom") == 0)
958 			tmp = 5;
959 		else if (expand_number($2, &tmp) != 0 ||
960 		    tmp > 15) {
961 			yyerror("invalid numeric value");
962 			free($2);
963 			return (1);
964 		}
965 
966 		lun_set_device_type(lun, tmp);
967 	}
968 	;
969 
970 lun_ctl_lun:	CTL_LUN STR
971 	{
972 		uint64_t tmp;
973 
974 		if (expand_number($2, &tmp) != 0) {
975 			yyerror("invalid numeric value");
976 			free($2);
977 			return (1);
978 		}
979 
980 		if (lun->l_ctl_lun >= 0) {
981 			log_warnx("ctl_lun for lun \"%s\" "
982 			    "specified more than once",
983 			    lun->l_name);
984 			return (1);
985 		}
986 		lun_set_ctl_lun(lun, tmp);
987 	}
988 	;
989 
990 lun_option:	OPTION STR STR
991 	{
992 		struct option *o;
993 
994 		o = option_new(&lun->l_options, $2, $3);
995 		free($2);
996 		free($3);
997 		if (o == NULL)
998 			return (1);
999 	}
1000 	;
1001 
1002 lun_path:	PATH STR
1003 	{
1004 		if (lun->l_path != NULL) {
1005 			log_warnx("path for lun \"%s\" "
1006 			    "specified more than once",
1007 			    lun->l_name);
1008 			free($2);
1009 			return (1);
1010 		}
1011 		lun_set_path(lun, $2);
1012 		free($2);
1013 	}
1014 	;
1015 
1016 lun_serial:	SERIAL STR
1017 	{
1018 		if (lun->l_serial != NULL) {
1019 			log_warnx("serial for lun \"%s\" "
1020 			    "specified more than once",
1021 			    lun->l_name);
1022 			free($2);
1023 			return (1);
1024 		}
1025 		lun_set_serial(lun, $2);
1026 		free($2);
1027 	}
1028 	;
1029 
1030 lun_size:	SIZE STR
1031 	{
1032 		uint64_t tmp;
1033 
1034 		if (expand_number($2, &tmp) != 0) {
1035 			yyerror("invalid numeric value");
1036 			free($2);
1037 			return (1);
1038 		}
1039 
1040 		if (lun->l_size != 0) {
1041 			log_warnx("size for lun \"%s\" "
1042 			    "specified more than once",
1043 			    lun->l_name);
1044 			return (1);
1045 		}
1046 		lun_set_size(lun, tmp);
1047 	}
1048 	;
1049 %%
1050 
1051 void
1052 yyerror(const char *str)
1053 {
1054 
1055 	log_warnx("error in configuration file at line %d near '%s': %s",
1056 	    lineno, yytext, str);
1057 }
1058 
1059 int
1060 parse_conf(struct conf *newconf, const char *path)
1061 {
1062 	int error;
1063 
1064 	conf = newconf;
1065 	yyin = fopen(path, "r");
1066 	if (yyin == NULL) {
1067 		log_warn("unable to open configuration file %s", path);
1068 		return (1);
1069 	}
1070 
1071 	lineno = 1;
1072 	yyrestart(yyin);
1073 	error = yyparse();
1074 	auth_group = NULL;
1075 	portal_group = NULL;
1076 	target = NULL;
1077 	lun = NULL;
1078 	fclose(yyin);
1079 
1080 	return (error);
1081 }
1082