xref: /freebsd/usr.sbin/ctld/parse.y (revision da5432eda807c4b7232d030d5157d5b417ea4f52)
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 		struct pport *pp;
836 		struct port *tp;
837 		int ret, i_pp, i_vp = 0;
838 
839 		ret = sscanf($2, "ioctl/%d/%d", &i_pp, &i_vp);
840 		if (ret > 0) {
841 			tp = port_new_ioctl(conf, target, i_pp, i_vp);
842 			if (tp == NULL) {
843 				log_warnx("can't create new ioctl port for "
844 				    "target \"%s\"", target->t_name);
845 				free($2);
846 				return (1);
847 			}
848 		} else {
849 			pp = pport_find(conf, $2);
850 			if (pp == NULL) {
851 				log_warnx("unknown port \"%s\" for target \"%s\"",
852 				    $2, target->t_name);
853 				free($2);
854 				return (1);
855 			}
856 			if (!TAILQ_EMPTY(&pp->pp_ports)) {
857 				log_warnx("can't link port \"%s\" to target \"%s\", "
858 				    "port already linked to some target",
859 				    $2, target->t_name);
860 				free($2);
861 				return (1);
862 			}
863 			tp = port_new_pp(conf, target, pp);
864 			if (tp == NULL) {
865 				log_warnx("can't link port \"%s\" to target \"%s\"",
866 				    $2, target->t_name);
867 				free($2);
868 				return (1);
869 			}
870 		}
871 
872 		free($2);
873 	}
874 	;
875 
876 target_redirect:	REDIRECT STR
877 	{
878 		int error;
879 
880 		error = target_set_redirection(target, $2);
881 		free($2);
882 		if (error != 0)
883 			return (1);
884 	}
885 	;
886 
887 target_lun:	LUN lun_number
888     OPENING_BRACKET lun_entries CLOSING_BRACKET
889 	{
890 		lun = NULL;
891 	}
892 	;
893 
894 lun_number:	STR
895 	{
896 		uint64_t tmp;
897 		int ret;
898 		char *name;
899 
900 		if (expand_number($1, &tmp) != 0) {
901 			yyerror("invalid numeric value");
902 			free($1);
903 			return (1);
904 		}
905 		if (tmp >= MAX_LUNS) {
906 			yyerror("LU number is too big");
907 			free($1);
908 			return (1);
909 		}
910 
911 		ret = asprintf(&name, "%s,lun,%ju", target->t_name, tmp);
912 		if (ret <= 0)
913 			log_err(1, "asprintf");
914 		lun = lun_new(conf, name);
915 		if (lun == NULL)
916 			return (1);
917 
918 		lun_set_scsiname(lun, name);
919 		target->t_luns[tmp] = lun;
920 	}
921 	;
922 
923 target_lun_ref:	LUN STR STR
924 	{
925 		uint64_t tmp;
926 
927 		if (expand_number($2, &tmp) != 0) {
928 			yyerror("invalid numeric value");
929 			free($2);
930 			free($3);
931 			return (1);
932 		}
933 		free($2);
934 		if (tmp >= MAX_LUNS) {
935 			yyerror("LU number is too big");
936 			free($3);
937 			return (1);
938 		}
939 
940 		lun = lun_find(conf, $3);
941 		free($3);
942 		if (lun == NULL)
943 			return (1);
944 
945 		target->t_luns[tmp] = lun;
946 	}
947 	;
948 
949 lun_entries:
950 	|
951 	lun_entries lun_entry
952 	|
953 	lun_entries lun_entry SEMICOLON
954 	;
955 
956 lun_entry:
957 	lun_backend
958 	|
959 	lun_blocksize
960 	|
961 	lun_device_id
962 	|
963 	lun_device_type
964 	|
965 	lun_ctl_lun
966 	|
967 	lun_option
968 	|
969 	lun_path
970 	|
971 	lun_serial
972 	|
973 	lun_size
974 	;
975 
976 lun_backend:	BACKEND STR
977 	{
978 		if (lun->l_backend != NULL) {
979 			log_warnx("backend for lun \"%s\" "
980 			    "specified more than once",
981 			    lun->l_name);
982 			free($2);
983 			return (1);
984 		}
985 		lun_set_backend(lun, $2);
986 		free($2);
987 	}
988 	;
989 
990 lun_blocksize:	BLOCKSIZE STR
991 	{
992 		uint64_t tmp;
993 
994 		if (expand_number($2, &tmp) != 0) {
995 			yyerror("invalid numeric value");
996 			free($2);
997 			return (1);
998 		}
999 
1000 		if (lun->l_blocksize != 0) {
1001 			log_warnx("blocksize for lun \"%s\" "
1002 			    "specified more than once",
1003 			    lun->l_name);
1004 			return (1);
1005 		}
1006 		lun_set_blocksize(lun, tmp);
1007 	}
1008 	;
1009 
1010 lun_device_id:	DEVICE_ID STR
1011 	{
1012 		if (lun->l_device_id != NULL) {
1013 			log_warnx("device_id for lun \"%s\" "
1014 			    "specified more than once",
1015 			    lun->l_name);
1016 			free($2);
1017 			return (1);
1018 		}
1019 		lun_set_device_id(lun, $2);
1020 		free($2);
1021 	}
1022 	;
1023 
1024 lun_device_type:	DEVICE_TYPE STR
1025 	{
1026 		uint64_t tmp;
1027 
1028 		if (strcasecmp($2, "disk") == 0 ||
1029 		    strcasecmp($2, "direct") == 0)
1030 			tmp = 0;
1031 		else if (strcasecmp($2, "processor") == 0)
1032 			tmp = 3;
1033 		else if (strcasecmp($2, "cd") == 0 ||
1034 		    strcasecmp($2, "cdrom") == 0 ||
1035 		    strcasecmp($2, "dvd") == 0 ||
1036 		    strcasecmp($2, "dvdrom") == 0)
1037 			tmp = 5;
1038 		else if (expand_number($2, &tmp) != 0 ||
1039 		    tmp > 15) {
1040 			yyerror("invalid numeric value");
1041 			free($2);
1042 			return (1);
1043 		}
1044 
1045 		lun_set_device_type(lun, tmp);
1046 	}
1047 	;
1048 
1049 lun_ctl_lun:	CTL_LUN STR
1050 	{
1051 		uint64_t tmp;
1052 
1053 		if (expand_number($2, &tmp) != 0) {
1054 			yyerror("invalid numeric value");
1055 			free($2);
1056 			return (1);
1057 		}
1058 
1059 		if (lun->l_ctl_lun >= 0) {
1060 			log_warnx("ctl_lun for lun \"%s\" "
1061 			    "specified more than once",
1062 			    lun->l_name);
1063 			return (1);
1064 		}
1065 		lun_set_ctl_lun(lun, tmp);
1066 	}
1067 	;
1068 
1069 lun_option:	OPTION STR STR
1070 	{
1071 		struct option *o;
1072 
1073 		o = option_new(&lun->l_options, $2, $3);
1074 		free($2);
1075 		free($3);
1076 		if (o == NULL)
1077 			return (1);
1078 	}
1079 	;
1080 
1081 lun_path:	PATH STR
1082 	{
1083 		if (lun->l_path != NULL) {
1084 			log_warnx("path for lun \"%s\" "
1085 			    "specified more than once",
1086 			    lun->l_name);
1087 			free($2);
1088 			return (1);
1089 		}
1090 		lun_set_path(lun, $2);
1091 		free($2);
1092 	}
1093 	;
1094 
1095 lun_serial:	SERIAL STR
1096 	{
1097 		if (lun->l_serial != NULL) {
1098 			log_warnx("serial for lun \"%s\" "
1099 			    "specified more than once",
1100 			    lun->l_name);
1101 			free($2);
1102 			return (1);
1103 		}
1104 		lun_set_serial(lun, $2);
1105 		free($2);
1106 	}
1107 	;
1108 
1109 lun_size:	SIZE STR
1110 	{
1111 		uint64_t tmp;
1112 
1113 		if (expand_number($2, &tmp) != 0) {
1114 			yyerror("invalid numeric value");
1115 			free($2);
1116 			return (1);
1117 		}
1118 
1119 		if (lun->l_size != 0) {
1120 			log_warnx("size for lun \"%s\" "
1121 			    "specified more than once",
1122 			    lun->l_name);
1123 			return (1);
1124 		}
1125 		lun_set_size(lun, tmp);
1126 	}
1127 	;
1128 %%
1129 
1130 void
1131 yyerror(const char *str)
1132 {
1133 
1134 	log_warnx("error in configuration file at line %d near '%s': %s",
1135 	    lineno, yytext, str);
1136 }
1137 
1138 int
1139 parse_conf(struct conf *newconf, const char *path)
1140 {
1141 	int error;
1142 
1143 	conf = newconf;
1144 	yyin = fopen(path, "r");
1145 	if (yyin == NULL) {
1146 		log_warn("unable to open configuration file %s", path);
1147 		return (1);
1148 	}
1149 
1150 	lineno = 1;
1151 	yyrestart(yyin);
1152 	error = yyparse();
1153 	auth_group = NULL;
1154 	portal_group = NULL;
1155 	target = NULL;
1156 	lun = NULL;
1157 	fclose(yyin);
1158 
1159 	return (error);
1160 }
1161