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