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