xref: /freebsd/usr.sbin/ctld/parse.y (revision 87b759f0fa1f7554d50ce640c40138512bbded44)
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/queue.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <assert.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 
40 #include "ctld.h"
41 #include <netinet/in.h>
42 #include <netinet/ip.h>
43 
44 extern FILE *yyin;
45 extern char *yytext;
46 extern int lineno;
47 
48 static struct conf *conf = NULL;
49 static struct auth_group *auth_group = NULL;
50 static struct portal_group *portal_group = NULL;
51 static struct target *target = NULL;
52 static struct lun *lun = NULL;
53 
54 extern void	yyerror(const char *);
55 extern void	yyrestart(FILE *);
56 
57 %}
58 
59 %token ALIAS AUTH_GROUP AUTH_TYPE BACKEND BLOCKSIZE CHAP CHAP_MUTUAL
60 %token CLOSING_BRACKET CTL_LUN DEBUG DEVICE_ID DEVICE_TYPE
61 %token DISCOVERY_AUTH_GROUP DISCOVERY_FILTER DSCP FOREIGN
62 %token INITIATOR_NAME INITIATOR_PORTAL ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT
63 %token LISTEN LISTEN_ISER LUN MAXPROC OFFLOAD OPENING_BRACKET OPTION
64 %token PATH PCP PIDFILE PORT PORTAL_GROUP REDIRECT SEMICOLON SERIAL
65 %token SIZE STR TAG TARGET TIMEOUT
66 %token AF11 AF12 AF13 AF21 AF22 AF23 AF31 AF32 AF33 AF41 AF42 AF43
67 %token BE EF CS0 CS1 CS2 CS3 CS4 CS5 CS6 CS7
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 	portal_group_dscp
358 	|
359 	portal_group_pcp
360 	;
361 
362 portal_group_discovery_auth_group:	DISCOVERY_AUTH_GROUP STR
363 	{
364 		if (portal_group->pg_discovery_auth_group != NULL) {
365 			log_warnx("discovery-auth-group for portal-group "
366 			    "\"%s\" specified more than once",
367 			    portal_group->pg_name);
368 			return (1);
369 		}
370 		portal_group->pg_discovery_auth_group =
371 		    auth_group_find(conf, $2);
372 		if (portal_group->pg_discovery_auth_group == NULL) {
373 			log_warnx("unknown discovery-auth-group \"%s\" "
374 			    "for portal-group \"%s\"",
375 			    $2, portal_group->pg_name);
376 			return (1);
377 		}
378 		free($2);
379 	}
380 	;
381 
382 portal_group_discovery_filter:	DISCOVERY_FILTER STR
383 	{
384 		int error;
385 
386 		error = portal_group_set_filter(portal_group, $2);
387 		free($2);
388 		if (error != 0)
389 			return (1);
390 	}
391 	;
392 
393 portal_group_foreign:	FOREIGN
394 	{
395 
396 		portal_group->pg_foreign = 1;
397 	}
398 	;
399 
400 portal_group_listen:	LISTEN STR
401 	{
402 		int error;
403 
404 		error = portal_group_add_listen(portal_group, $2, false);
405 		free($2);
406 		if (error != 0)
407 			return (1);
408 	}
409 	;
410 
411 portal_group_listen_iser:	LISTEN_ISER STR
412 	{
413 		int error;
414 
415 		error = portal_group_add_listen(portal_group, $2, true);
416 		free($2);
417 		if (error != 0)
418 			return (1);
419 	}
420 	;
421 
422 portal_group_offload:	OFFLOAD STR
423 	{
424 		int error;
425 
426 		error = portal_group_set_offload(portal_group, $2);
427 		free($2);
428 		if (error != 0)
429 			return (1);
430 	}
431 	;
432 
433 portal_group_option:	OPTION STR STR
434 	{
435 		struct option *o;
436 
437 		o = option_new(&portal_group->pg_options, $2, $3);
438 		free($2);
439 		free($3);
440 		if (o == NULL)
441 			return (1);
442 	}
443 	;
444 
445 portal_group_redirect:	REDIRECT STR
446 	{
447 		int error;
448 
449 		error = portal_group_set_redirection(portal_group, $2);
450 		free($2);
451 		if (error != 0)
452 			return (1);
453 	}
454 	;
455 
456 portal_group_tag:	TAG STR
457 	{
458 		uint64_t tmp;
459 
460 		if (expand_number($2, &tmp) != 0) {
461 			yyerror("invalid numeric value");
462 			free($2);
463 			return (1);
464 		}
465 
466 		portal_group->pg_tag = tmp;
467 	}
468 	;
469 
470 portal_group_dscp
471 : DSCP STR
472 	{
473 		uint64_t tmp;
474 
475 		if (strcmp($2, "0x") == 0) {
476 			tmp = strtol($2 + 2, NULL, 16);
477 		} else if (expand_number($2, &tmp) != 0) {
478 			yyerror("invalid numeric value");
479 			free($2);
480 			return(1);
481 		}
482 		if (tmp >= 0x40) {
483 			yyerror("invalid dscp value");
484 			return(1);
485 		}
486 
487 		portal_group->pg_dscp = tmp;
488 	}
489 | DSCP BE	{ portal_group->pg_dscp = IPTOS_DSCP_CS0  >> 2 ; }
490 | DSCP EF	{ portal_group->pg_dscp = IPTOS_DSCP_EF   >> 2 ; }
491 | DSCP CS0	{ portal_group->pg_dscp = IPTOS_DSCP_CS0  >> 2 ; }
492 | DSCP CS1	{ portal_group->pg_dscp = IPTOS_DSCP_CS1  >> 2 ; }
493 | DSCP CS2	{ portal_group->pg_dscp = IPTOS_DSCP_CS2  >> 2 ; }
494 | DSCP CS3	{ portal_group->pg_dscp = IPTOS_DSCP_CS3  >> 2 ; }
495 | DSCP CS4	{ portal_group->pg_dscp = IPTOS_DSCP_CS4  >> 2 ; }
496 | DSCP CS5	{ portal_group->pg_dscp = IPTOS_DSCP_CS5  >> 2 ; }
497 | DSCP CS6	{ portal_group->pg_dscp = IPTOS_DSCP_CS6  >> 2 ; }
498 | DSCP CS7	{ portal_group->pg_dscp = IPTOS_DSCP_CS7  >> 2 ; }
499 | DSCP AF11	{ portal_group->pg_dscp = IPTOS_DSCP_AF11 >> 2 ; }
500 | DSCP AF12	{ portal_group->pg_dscp = IPTOS_DSCP_AF12 >> 2 ; }
501 | DSCP AF13	{ portal_group->pg_dscp = IPTOS_DSCP_AF13 >> 2 ; }
502 | DSCP AF21	{ portal_group->pg_dscp = IPTOS_DSCP_AF21 >> 2 ; }
503 | DSCP AF22	{ portal_group->pg_dscp = IPTOS_DSCP_AF22 >> 2 ; }
504 | DSCP AF23	{ portal_group->pg_dscp = IPTOS_DSCP_AF23 >> 2 ; }
505 | DSCP AF31	{ portal_group->pg_dscp = IPTOS_DSCP_AF31 >> 2 ; }
506 | DSCP AF32	{ portal_group->pg_dscp = IPTOS_DSCP_AF32 >> 2 ; }
507 | DSCP AF33	{ portal_group->pg_dscp = IPTOS_DSCP_AF33 >> 2 ; }
508 | DSCP AF41	{ portal_group->pg_dscp = IPTOS_DSCP_AF41 >> 2 ; }
509 | DSCP AF42	{ portal_group->pg_dscp = IPTOS_DSCP_AF42 >> 2 ; }
510 | DSCP AF43	{ portal_group->pg_dscp = IPTOS_DSCP_AF43 >> 2 ; }
511 	;
512 
513 portal_group_pcp:	PCP STR
514 	{
515 		uint64_t tmp;
516 
517 		if (expand_number($2, &tmp) != 0) {
518 			yyerror("invalid numeric value");
519 			free($2);
520 			return (1);
521 		}
522 		if (tmp > 7) {
523 			yyerror("invalid pcp value");
524 			free($2);
525 			return (1);
526 		}
527 
528 		portal_group->pg_pcp = tmp;
529 	}
530 	;
531 
532 lun:	LUN lun_name
533     OPENING_BRACKET lun_entries CLOSING_BRACKET
534 	{
535 		lun = NULL;
536 	}
537 	;
538 
539 lun_name:	STR
540 	{
541 		lun = lun_new(conf, $1);
542 		free($1);
543 		if (lun == NULL)
544 			return (1);
545 	}
546 	;
547 
548 target:	TARGET target_name
549     OPENING_BRACKET target_entries CLOSING_BRACKET
550 	{
551 		target = NULL;
552 	}
553 	;
554 
555 target_name:	STR
556 	{
557 		target = target_new(conf, $1);
558 		free($1);
559 		if (target == NULL)
560 			return (1);
561 	}
562 	;
563 
564 target_entries:
565 	|
566 	target_entries target_entry
567 	|
568 	target_entries target_entry SEMICOLON
569 	;
570 
571 target_entry:
572 	target_alias
573 	|
574 	target_auth_group
575 	|
576 	target_auth_type
577 	|
578 	target_chap
579 	|
580 	target_chap_mutual
581 	|
582 	target_initiator_name
583 	|
584 	target_initiator_portal
585 	|
586 	target_portal_group
587 	|
588 	target_port
589 	|
590 	target_redirect
591 	|
592 	target_lun
593 	|
594 	target_lun_ref
595 	;
596 
597 target_alias:	ALIAS STR
598 	{
599 		if (target->t_alias != NULL) {
600 			log_warnx("alias for target \"%s\" "
601 			    "specified more than once", target->t_name);
602 			return (1);
603 		}
604 		target->t_alias = $2;
605 	}
606 	;
607 
608 target_auth_group:	AUTH_GROUP STR
609 	{
610 		if (target->t_auth_group != NULL) {
611 			if (target->t_auth_group->ag_name != NULL)
612 				log_warnx("auth-group for target \"%s\" "
613 				    "specified more than once", target->t_name);
614 			else
615 				log_warnx("cannot use both auth-group and explicit "
616 				    "authorisations for target \"%s\"",
617 				    target->t_name);
618 			return (1);
619 		}
620 		target->t_auth_group = auth_group_find(conf, $2);
621 		if (target->t_auth_group == NULL) {
622 			log_warnx("unknown auth-group \"%s\" for target "
623 			    "\"%s\"", $2, target->t_name);
624 			return (1);
625 		}
626 		free($2);
627 	}
628 	;
629 
630 target_auth_type:	AUTH_TYPE STR
631 	{
632 		int error;
633 
634 		if (target->t_auth_group != NULL) {
635 			if (target->t_auth_group->ag_name != NULL) {
636 				log_warnx("cannot use both auth-group and "
637 				    "auth-type for target \"%s\"",
638 				    target->t_name);
639 				return (1);
640 			}
641 		} else {
642 			target->t_auth_group = auth_group_new(conf, NULL);
643 			if (target->t_auth_group == NULL) {
644 				free($2);
645 				return (1);
646 			}
647 			target->t_auth_group->ag_target = target;
648 		}
649 		error = auth_group_set_type(target->t_auth_group, $2);
650 		free($2);
651 		if (error != 0)
652 			return (1);
653 	}
654 	;
655 
656 target_chap:	CHAP STR STR
657 	{
658 		const struct auth *ca;
659 
660 		if (target->t_auth_group != NULL) {
661 			if (target->t_auth_group->ag_name != NULL) {
662 				log_warnx("cannot use both auth-group and "
663 				    "chap for target \"%s\"",
664 				    target->t_name);
665 				free($2);
666 				free($3);
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 				free($3);
674 				return (1);
675 			}
676 			target->t_auth_group->ag_target = target;
677 		}
678 		ca = auth_new_chap(target->t_auth_group, $2, $3);
679 		free($2);
680 		free($3);
681 		if (ca == NULL)
682 			return (1);
683 	}
684 	;
685 
686 target_chap_mutual:	CHAP_MUTUAL STR STR STR STR
687 	{
688 		const struct auth *ca;
689 
690 		if (target->t_auth_group != NULL) {
691 			if (target->t_auth_group->ag_name != NULL) {
692 				log_warnx("cannot use both auth-group and "
693 				    "chap-mutual for target \"%s\"",
694 				    target->t_name);
695 				free($2);
696 				free($3);
697 				free($4);
698 				free($5);
699 				return (1);
700 			}
701 		} else {
702 			target->t_auth_group = auth_group_new(conf, NULL);
703 			if (target->t_auth_group == NULL) {
704 				free($2);
705 				free($3);
706 				free($4);
707 				free($5);
708 				return (1);
709 			}
710 			target->t_auth_group->ag_target = target;
711 		}
712 		ca = auth_new_chap_mutual(target->t_auth_group,
713 		    $2, $3, $4, $5);
714 		free($2);
715 		free($3);
716 		free($4);
717 		free($5);
718 		if (ca == NULL)
719 			return (1);
720 	}
721 	;
722 
723 target_initiator_name:	INITIATOR_NAME STR
724 	{
725 		const struct auth_name *an;
726 
727 		if (target->t_auth_group != NULL) {
728 			if (target->t_auth_group->ag_name != NULL) {
729 				log_warnx("cannot use both auth-group and "
730 				    "initiator-name for target \"%s\"",
731 				    target->t_name);
732 				free($2);
733 				return (1);
734 			}
735 		} else {
736 			target->t_auth_group = auth_group_new(conf, NULL);
737 			if (target->t_auth_group == NULL) {
738 				free($2);
739 				return (1);
740 			}
741 			target->t_auth_group->ag_target = target;
742 		}
743 		an = auth_name_new(target->t_auth_group, $2);
744 		free($2);
745 		if (an == NULL)
746 			return (1);
747 	}
748 	;
749 
750 target_initiator_portal:	INITIATOR_PORTAL STR
751 	{
752 		const struct auth_portal *ap;
753 
754 		if (target->t_auth_group != NULL) {
755 			if (target->t_auth_group->ag_name != NULL) {
756 				log_warnx("cannot use both auth-group and "
757 				    "initiator-portal for target \"%s\"",
758 				    target->t_name);
759 				free($2);
760 				return (1);
761 			}
762 		} else {
763 			target->t_auth_group = auth_group_new(conf, NULL);
764 			if (target->t_auth_group == NULL) {
765 				free($2);
766 				return (1);
767 			}
768 			target->t_auth_group->ag_target = target;
769 		}
770 		ap = auth_portal_new(target->t_auth_group, $2);
771 		free($2);
772 		if (ap == NULL)
773 			return (1);
774 	}
775 	;
776 
777 target_portal_group:	PORTAL_GROUP STR STR
778 	{
779 		struct portal_group *tpg;
780 		struct auth_group *tag;
781 		struct port *tp;
782 
783 		tpg = portal_group_find(conf, $2);
784 		if (tpg == NULL) {
785 			log_warnx("unknown portal-group \"%s\" for target "
786 			    "\"%s\"", $2, target->t_name);
787 			free($2);
788 			free($3);
789 			return (1);
790 		}
791 		tag = auth_group_find(conf, $3);
792 		if (tag == NULL) {
793 			log_warnx("unknown auth-group \"%s\" for target "
794 			    "\"%s\"", $3, target->t_name);
795 			free($2);
796 			free($3);
797 			return (1);
798 		}
799 		tp = port_new(conf, target, tpg);
800 		if (tp == NULL) {
801 			log_warnx("can't link portal-group \"%s\" to target "
802 			    "\"%s\"", $2, target->t_name);
803 			free($2);
804 			return (1);
805 		}
806 		tp->p_auth_group = tag;
807 		free($2);
808 		free($3);
809 	}
810 	|		PORTAL_GROUP STR
811 	{
812 		struct portal_group *tpg;
813 		struct port *tp;
814 
815 		tpg = portal_group_find(conf, $2);
816 		if (tpg == NULL) {
817 			log_warnx("unknown portal-group \"%s\" for target "
818 			    "\"%s\"", $2, target->t_name);
819 			free($2);
820 			return (1);
821 		}
822 		tp = port_new(conf, target, tpg);
823 		if (tp == NULL) {
824 			log_warnx("can't link portal-group \"%s\" to target "
825 			    "\"%s\"", $2, target->t_name);
826 			free($2);
827 			return (1);
828 		}
829 		free($2);
830 	}
831 	;
832 
833 target_port:	PORT STR
834 	{
835 		target->t_pport = strdup($2);
836 
837 		free($2);
838 	}
839 	;
840 
841 target_redirect:	REDIRECT STR
842 	{
843 		int error;
844 
845 		error = target_set_redirection(target, $2);
846 		free($2);
847 		if (error != 0)
848 			return (1);
849 	}
850 	;
851 
852 target_lun:	LUN lun_number
853     OPENING_BRACKET lun_entries CLOSING_BRACKET
854 	{
855 		lun = NULL;
856 	}
857 	;
858 
859 lun_number:	STR
860 	{
861 		uint64_t tmp;
862 		int ret;
863 		char *name;
864 
865 		if (expand_number($1, &tmp) != 0) {
866 			yyerror("invalid numeric value");
867 			free($1);
868 			return (1);
869 		}
870 		if (tmp >= MAX_LUNS) {
871 			yyerror("LU number is too big");
872 			free($1);
873 			return (1);
874 		}
875 
876 		ret = asprintf(&name, "%s,lun,%ju", target->t_name, tmp);
877 		if (ret <= 0)
878 			log_err(1, "asprintf");
879 		lun = lun_new(conf, name);
880 		if (lun == NULL)
881 			return (1);
882 
883 		lun_set_scsiname(lun, name);
884 		target->t_luns[tmp] = lun;
885 	}
886 	;
887 
888 target_lun_ref:	LUN STR STR
889 	{
890 		uint64_t tmp;
891 
892 		if (expand_number($2, &tmp) != 0) {
893 			yyerror("invalid numeric value");
894 			free($2);
895 			free($3);
896 			return (1);
897 		}
898 		free($2);
899 		if (tmp >= MAX_LUNS) {
900 			yyerror("LU number is too big");
901 			free($3);
902 			return (1);
903 		}
904 
905 		lun = lun_find(conf, $3);
906 		free($3);
907 		if (lun == NULL)
908 			return (1);
909 
910 		target->t_luns[tmp] = lun;
911 	}
912 	;
913 
914 lun_entries:
915 	|
916 	lun_entries lun_entry
917 	|
918 	lun_entries lun_entry SEMICOLON
919 	;
920 
921 lun_entry:
922 	lun_backend
923 	|
924 	lun_blocksize
925 	|
926 	lun_device_id
927 	|
928 	lun_device_type
929 	|
930 	lun_ctl_lun
931 	|
932 	lun_option
933 	|
934 	lun_path
935 	|
936 	lun_serial
937 	|
938 	lun_size
939 	;
940 
941 lun_backend:	BACKEND STR
942 	{
943 		if (lun->l_backend != NULL) {
944 			log_warnx("backend for lun \"%s\" "
945 			    "specified more than once",
946 			    lun->l_name);
947 			free($2);
948 			return (1);
949 		}
950 		lun_set_backend(lun, $2);
951 		free($2);
952 	}
953 	;
954 
955 lun_blocksize:	BLOCKSIZE STR
956 	{
957 		uint64_t tmp;
958 
959 		if (expand_number($2, &tmp) != 0) {
960 			yyerror("invalid numeric value");
961 			free($2);
962 			return (1);
963 		}
964 
965 		if (lun->l_blocksize != 0) {
966 			log_warnx("blocksize for lun \"%s\" "
967 			    "specified more than once",
968 			    lun->l_name);
969 			return (1);
970 		}
971 		lun_set_blocksize(lun, tmp);
972 	}
973 	;
974 
975 lun_device_id:	DEVICE_ID STR
976 	{
977 		if (lun->l_device_id != NULL) {
978 			log_warnx("device_id for lun \"%s\" "
979 			    "specified more than once",
980 			    lun->l_name);
981 			free($2);
982 			return (1);
983 		}
984 		lun_set_device_id(lun, $2);
985 		free($2);
986 	}
987 	;
988 
989 lun_device_type:	DEVICE_TYPE STR
990 	{
991 		uint64_t tmp;
992 
993 		if (strcasecmp($2, "disk") == 0 ||
994 		    strcasecmp($2, "direct") == 0)
995 			tmp = 0;
996 		else if (strcasecmp($2, "processor") == 0)
997 			tmp = 3;
998 		else if (strcasecmp($2, "cd") == 0 ||
999 		    strcasecmp($2, "cdrom") == 0 ||
1000 		    strcasecmp($2, "dvd") == 0 ||
1001 		    strcasecmp($2, "dvdrom") == 0)
1002 			tmp = 5;
1003 		else if (expand_number($2, &tmp) != 0 ||
1004 		    tmp > 15) {
1005 			yyerror("invalid numeric value");
1006 			free($2);
1007 			return (1);
1008 		}
1009 
1010 		lun_set_device_type(lun, tmp);
1011 	}
1012 	;
1013 
1014 lun_ctl_lun:	CTL_LUN STR
1015 	{
1016 		uint64_t tmp;
1017 
1018 		if (expand_number($2, &tmp) != 0) {
1019 			yyerror("invalid numeric value");
1020 			free($2);
1021 			return (1);
1022 		}
1023 
1024 		if (lun->l_ctl_lun >= 0) {
1025 			log_warnx("ctl_lun for lun \"%s\" "
1026 			    "specified more than once",
1027 			    lun->l_name);
1028 			return (1);
1029 		}
1030 		lun_set_ctl_lun(lun, tmp);
1031 	}
1032 	;
1033 
1034 lun_option:	OPTION STR STR
1035 	{
1036 		struct option *o;
1037 
1038 		o = option_new(&lun->l_options, $2, $3);
1039 		free($2);
1040 		free($3);
1041 		if (o == NULL)
1042 			return (1);
1043 	}
1044 	;
1045 
1046 lun_path:	PATH STR
1047 	{
1048 		if (lun->l_path != NULL) {
1049 			log_warnx("path for lun \"%s\" "
1050 			    "specified more than once",
1051 			    lun->l_name);
1052 			free($2);
1053 			return (1);
1054 		}
1055 		lun_set_path(lun, $2);
1056 		free($2);
1057 	}
1058 	;
1059 
1060 lun_serial:	SERIAL STR
1061 	{
1062 		if (lun->l_serial != NULL) {
1063 			log_warnx("serial for lun \"%s\" "
1064 			    "specified more than once",
1065 			    lun->l_name);
1066 			free($2);
1067 			return (1);
1068 		}
1069 		lun_set_serial(lun, $2);
1070 		free($2);
1071 	}
1072 	;
1073 
1074 lun_size:	SIZE STR
1075 	{
1076 		uint64_t tmp;
1077 
1078 		if (expand_number($2, &tmp) != 0) {
1079 			yyerror("invalid numeric value");
1080 			free($2);
1081 			return (1);
1082 		}
1083 
1084 		if (lun->l_size != 0) {
1085 			log_warnx("size for lun \"%s\" "
1086 			    "specified more than once",
1087 			    lun->l_name);
1088 			return (1);
1089 		}
1090 		lun_set_size(lun, tmp);
1091 	}
1092 	;
1093 %%
1094 
1095 void
1096 yyerror(const char *str)
1097 {
1098 
1099 	log_warnx("error in configuration file at line %d near '%s': %s",
1100 	    lineno, yytext, str);
1101 }
1102 
1103 int
1104 parse_conf(struct conf *newconf, const char *path)
1105 {
1106 	int error;
1107 
1108 	conf = newconf;
1109 	yyin = fopen(path, "r");
1110 	if (yyin == NULL) {
1111 		log_warn("unable to open configuration file %s", path);
1112 		return (1);
1113 	}
1114 
1115 	lineno = 1;
1116 	yyrestart(yyin);
1117 	error = yyparse();
1118 	auth_group = NULL;
1119 	portal_group = NULL;
1120 	target = NULL;
1121 	lun = NULL;
1122 	fclose(yyin);
1123 
1124 	return (error);
1125 }
1126