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