xref: /freebsd/usr.sbin/ctld/parse.y (revision 7cc42f6d)
1 %{
2 /*-
3  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4  *
5  * Copyright (c) 2012 The FreeBSD Foundation
6  * All rights reserved.
7  *
8  * This software was developed by Edward Tomasz Napierala under sponsorship
9  * from the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD$
33  */
34 
35 #include <sys/queue.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <assert.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 #include "ctld.h"
44 #include <netinet/in.h>
45 #include <netinet/ip.h>
46 
47 extern FILE *yyin;
48 extern char *yytext;
49 extern int lineno;
50 
51 static struct conf *conf = NULL;
52 static struct auth_group *auth_group = NULL;
53 static struct portal_group *portal_group = NULL;
54 static struct target *target = NULL;
55 static struct lun *lun = NULL;
56 
57 extern void	yyerror(const char *);
58 extern int	yylex(void);
59 extern void	yyrestart(FILE *);
60 
61 %}
62 
63 %token ALIAS AUTH_GROUP AUTH_TYPE BACKEND BLOCKSIZE CHAP CHAP_MUTUAL
64 %token CLOSING_BRACKET CTL_LUN DEBUG DEVICE_ID DEVICE_TYPE
65 %token DISCOVERY_AUTH_GROUP DISCOVERY_FILTER DSCP FOREIGN
66 %token INITIATOR_NAME INITIATOR_PORTAL ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT
67 %token LISTEN LISTEN_ISER LUN MAXPROC OFFLOAD OPENING_BRACKET OPTION
68 %token PATH PIDFILE PORT PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR
69 %token TAG TARGET TIMEOUT
70 %token AF11 AF12 AF13 AF21 AF22 AF23 AF31 AF32 AF33 AF41 AF42 AF43
71 %token BE EF CS0 CS1 CS2 CS3 CS4 CS5 CS6 CS7
72 
73 %union
74 {
75 	char *str;
76 }
77 
78 %token <str> STR
79 
80 %%
81 
82 statements:
83 	|
84 	statements statement
85 	|
86 	statements statement SEMICOLON
87 	;
88 
89 statement:
90 	debug
91 	|
92 	timeout
93 	|
94 	maxproc
95 	|
96 	pidfile
97 	|
98 	isns_server
99 	|
100 	isns_period
101 	|
102 	isns_timeout
103 	|
104 	auth_group
105 	|
106 	portal_group
107 	|
108 	lun
109 	|
110 	target
111 	;
112 
113 debug:		DEBUG STR
114 	{
115 		uint64_t tmp;
116 
117 		if (expand_number($2, &tmp) != 0) {
118 			yyerror("invalid numeric value");
119 			free($2);
120 			return (1);
121 		}
122 
123 		conf->conf_debug = tmp;
124 	}
125 	;
126 
127 timeout:	TIMEOUT STR
128 	{
129 		uint64_t tmp;
130 
131 		if (expand_number($2, &tmp) != 0) {
132 			yyerror("invalid numeric value");
133 			free($2);
134 			return (1);
135 		}
136 
137 		conf->conf_timeout = tmp;
138 	}
139 	;
140 
141 maxproc:	MAXPROC STR
142 	{
143 		uint64_t tmp;
144 
145 		if (expand_number($2, &tmp) != 0) {
146 			yyerror("invalid numeric value");
147 			free($2);
148 			return (1);
149 		}
150 
151 		conf->conf_maxproc = tmp;
152 	}
153 	;
154 
155 pidfile:	PIDFILE STR
156 	{
157 		if (conf->conf_pidfile_path != NULL) {
158 			log_warnx("pidfile specified more than once");
159 			free($2);
160 			return (1);
161 		}
162 		conf->conf_pidfile_path = $2;
163 	}
164 	;
165 
166 isns_server:	ISNS_SERVER STR
167 	{
168 		int error;
169 
170 		error = isns_new(conf, $2);
171 		free($2);
172 		if (error != 0)
173 			return (1);
174 	}
175 	;
176 
177 isns_period:	ISNS_PERIOD STR
178 	{
179 		uint64_t tmp;
180 
181 		if (expand_number($2, &tmp) != 0) {
182 			yyerror("invalid numeric value");
183 			free($2);
184 			return (1);
185 		}
186 
187 		conf->conf_isns_period = tmp;
188 	}
189 	;
190 
191 isns_timeout:	ISNS_TIMEOUT STR
192 	{
193 		uint64_t tmp;
194 
195 		if (expand_number($2, &tmp) != 0) {
196 			yyerror("invalid numeric value");
197 			free($2);
198 			return (1);
199 		}
200 
201 		conf->conf_isns_timeout = tmp;
202 	}
203 	;
204 
205 auth_group:	AUTH_GROUP auth_group_name
206     OPENING_BRACKET auth_group_entries CLOSING_BRACKET
207 	{
208 		auth_group = NULL;
209 	}
210 	;
211 
212 auth_group_name:	STR
213 	{
214 		/*
215 		 * Make it possible to redefine default
216 		 * auth-group. but only once.
217 		 */
218 		if (strcmp($1, "default") == 0 &&
219 		    conf->conf_default_ag_defined == false) {
220 			auth_group = auth_group_find(conf, $1);
221 			conf->conf_default_ag_defined = true;
222 		} else {
223 			auth_group = auth_group_new(conf, $1);
224 		}
225 		free($1);
226 		if (auth_group == NULL)
227 			return (1);
228 	}
229 	;
230 
231 auth_group_entries:
232 	|
233 	auth_group_entries auth_group_entry
234 	|
235 	auth_group_entries auth_group_entry SEMICOLON
236 	;
237 
238 auth_group_entry:
239 	auth_group_auth_type
240 	|
241 	auth_group_chap
242 	|
243 	auth_group_chap_mutual
244 	|
245 	auth_group_initiator_name
246 	|
247 	auth_group_initiator_portal
248 	;
249 
250 auth_group_auth_type:	AUTH_TYPE STR
251 	{
252 		int error;
253 
254 		error = auth_group_set_type(auth_group, $2);
255 		free($2);
256 		if (error != 0)
257 			return (1);
258 	}
259 	;
260 
261 auth_group_chap:	CHAP STR STR
262 	{
263 		const struct auth *ca;
264 
265 		ca = auth_new_chap(auth_group, $2, $3);
266 		free($2);
267 		free($3);
268 		if (ca == NULL)
269 			return (1);
270 	}
271 	;
272 
273 auth_group_chap_mutual:	CHAP_MUTUAL STR STR STR STR
274 	{
275 		const struct auth *ca;
276 
277 		ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5);
278 		free($2);
279 		free($3);
280 		free($4);
281 		free($5);
282 		if (ca == NULL)
283 			return (1);
284 	}
285 	;
286 
287 auth_group_initiator_name:	INITIATOR_NAME STR
288 	{
289 		const struct auth_name *an;
290 
291 		an = auth_name_new(auth_group, $2);
292 		free($2);
293 		if (an == NULL)
294 			return (1);
295 	}
296 	;
297 
298 auth_group_initiator_portal:	INITIATOR_PORTAL STR
299 	{
300 		const struct auth_portal *ap;
301 
302 		ap = auth_portal_new(auth_group, $2);
303 		free($2);
304 		if (ap == NULL)
305 			return (1);
306 	}
307 	;
308 
309 portal_group:	PORTAL_GROUP portal_group_name
310     OPENING_BRACKET portal_group_entries CLOSING_BRACKET
311 	{
312 		portal_group = NULL;
313 	}
314 	;
315 
316 portal_group_name:	STR
317 	{
318 		/*
319 		 * Make it possible to redefine default
320 		 * portal-group. but only once.
321 		 */
322 		if (strcmp($1, "default") == 0 &&
323 		    conf->conf_default_pg_defined == false) {
324 			portal_group = portal_group_find(conf, $1);
325 			conf->conf_default_pg_defined = true;
326 		} else {
327 			portal_group = portal_group_new(conf, $1);
328 		}
329 		free($1);
330 		if (portal_group == NULL)
331 			return (1);
332 	}
333 	;
334 
335 portal_group_entries:
336 	|
337 	portal_group_entries portal_group_entry
338 	|
339 	portal_group_entries portal_group_entry SEMICOLON
340 	;
341 
342 portal_group_entry:
343 	portal_group_discovery_auth_group
344 	|
345 	portal_group_discovery_filter
346 	|
347 	portal_group_foreign
348 	|
349 	portal_group_listen
350 	|
351 	portal_group_listen_iser
352 	|
353 	portal_group_offload
354 	|
355 	portal_group_option
356 	|
357 	portal_group_redirect
358 	|
359 	portal_group_tag
360 	|
361 	portal_group_dscp
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 
516 lun:	LUN lun_name
517     OPENING_BRACKET lun_entries CLOSING_BRACKET
518 	{
519 		lun = NULL;
520 	}
521 	;
522 
523 lun_name:	STR
524 	{
525 		lun = lun_new(conf, $1);
526 		free($1);
527 		if (lun == NULL)
528 			return (1);
529 	}
530 	;
531 
532 target:	TARGET target_name
533     OPENING_BRACKET target_entries CLOSING_BRACKET
534 	{
535 		target = NULL;
536 	}
537 	;
538 
539 target_name:	STR
540 	{
541 		target = target_new(conf, $1);
542 		free($1);
543 		if (target == NULL)
544 			return (1);
545 	}
546 	;
547 
548 target_entries:
549 	|
550 	target_entries target_entry
551 	|
552 	target_entries target_entry SEMICOLON
553 	;
554 
555 target_entry:
556 	target_alias
557 	|
558 	target_auth_group
559 	|
560 	target_auth_type
561 	|
562 	target_chap
563 	|
564 	target_chap_mutual
565 	|
566 	target_initiator_name
567 	|
568 	target_initiator_portal
569 	|
570 	target_portal_group
571 	|
572 	target_port
573 	|
574 	target_redirect
575 	|
576 	target_lun
577 	|
578 	target_lun_ref
579 	;
580 
581 target_alias:	ALIAS STR
582 	{
583 		if (target->t_alias != NULL) {
584 			log_warnx("alias for target \"%s\" "
585 			    "specified more than once", target->t_name);
586 			return (1);
587 		}
588 		target->t_alias = $2;
589 	}
590 	;
591 
592 target_auth_group:	AUTH_GROUP STR
593 	{
594 		if (target->t_auth_group != NULL) {
595 			if (target->t_auth_group->ag_name != NULL)
596 				log_warnx("auth-group for target \"%s\" "
597 				    "specified more than once", target->t_name);
598 			else
599 				log_warnx("cannot use both auth-group and explicit "
600 				    "authorisations for target \"%s\"",
601 				    target->t_name);
602 			return (1);
603 		}
604 		target->t_auth_group = auth_group_find(conf, $2);
605 		if (target->t_auth_group == NULL) {
606 			log_warnx("unknown auth-group \"%s\" for target "
607 			    "\"%s\"", $2, target->t_name);
608 			return (1);
609 		}
610 		free($2);
611 	}
612 	;
613 
614 target_auth_type:	AUTH_TYPE STR
615 	{
616 		int error;
617 
618 		if (target->t_auth_group != NULL) {
619 			if (target->t_auth_group->ag_name != NULL) {
620 				log_warnx("cannot use both auth-group and "
621 				    "auth-type for target \"%s\"",
622 				    target->t_name);
623 				return (1);
624 			}
625 		} else {
626 			target->t_auth_group = auth_group_new(conf, NULL);
627 			if (target->t_auth_group == NULL) {
628 				free($2);
629 				return (1);
630 			}
631 			target->t_auth_group->ag_target = target;
632 		}
633 		error = auth_group_set_type(target->t_auth_group, $2);
634 		free($2);
635 		if (error != 0)
636 			return (1);
637 	}
638 	;
639 
640 target_chap:	CHAP STR STR
641 	{
642 		const struct auth *ca;
643 
644 		if (target->t_auth_group != NULL) {
645 			if (target->t_auth_group->ag_name != NULL) {
646 				log_warnx("cannot use both auth-group and "
647 				    "chap for target \"%s\"",
648 				    target->t_name);
649 				free($2);
650 				free($3);
651 				return (1);
652 			}
653 		} else {
654 			target->t_auth_group = auth_group_new(conf, NULL);
655 			if (target->t_auth_group == NULL) {
656 				free($2);
657 				free($3);
658 				return (1);
659 			}
660 			target->t_auth_group->ag_target = target;
661 		}
662 		ca = auth_new_chap(target->t_auth_group, $2, $3);
663 		free($2);
664 		free($3);
665 		if (ca == NULL)
666 			return (1);
667 	}
668 	;
669 
670 target_chap_mutual:	CHAP_MUTUAL STR STR STR STR
671 	{
672 		const struct auth *ca;
673 
674 		if (target->t_auth_group != NULL) {
675 			if (target->t_auth_group->ag_name != NULL) {
676 				log_warnx("cannot use both auth-group and "
677 				    "chap-mutual for target \"%s\"",
678 				    target->t_name);
679 				free($2);
680 				free($3);
681 				free($4);
682 				free($5);
683 				return (1);
684 			}
685 		} else {
686 			target->t_auth_group = auth_group_new(conf, NULL);
687 			if (target->t_auth_group == NULL) {
688 				free($2);
689 				free($3);
690 				free($4);
691 				free($5);
692 				return (1);
693 			}
694 			target->t_auth_group->ag_target = target;
695 		}
696 		ca = auth_new_chap_mutual(target->t_auth_group,
697 		    $2, $3, $4, $5);
698 		free($2);
699 		free($3);
700 		free($4);
701 		free($5);
702 		if (ca == NULL)
703 			return (1);
704 	}
705 	;
706 
707 target_initiator_name:	INITIATOR_NAME STR
708 	{
709 		const struct auth_name *an;
710 
711 		if (target->t_auth_group != NULL) {
712 			if (target->t_auth_group->ag_name != NULL) {
713 				log_warnx("cannot use both auth-group and "
714 				    "initiator-name for target \"%s\"",
715 				    target->t_name);
716 				free($2);
717 				return (1);
718 			}
719 		} else {
720 			target->t_auth_group = auth_group_new(conf, NULL);
721 			if (target->t_auth_group == NULL) {
722 				free($2);
723 				return (1);
724 			}
725 			target->t_auth_group->ag_target = target;
726 		}
727 		an = auth_name_new(target->t_auth_group, $2);
728 		free($2);
729 		if (an == NULL)
730 			return (1);
731 	}
732 	;
733 
734 target_initiator_portal:	INITIATOR_PORTAL STR
735 	{
736 		const struct auth_portal *ap;
737 
738 		if (target->t_auth_group != NULL) {
739 			if (target->t_auth_group->ag_name != NULL) {
740 				log_warnx("cannot use both auth-group and "
741 				    "initiator-portal for target \"%s\"",
742 				    target->t_name);
743 				free($2);
744 				return (1);
745 			}
746 		} else {
747 			target->t_auth_group = auth_group_new(conf, NULL);
748 			if (target->t_auth_group == NULL) {
749 				free($2);
750 				return (1);
751 			}
752 			target->t_auth_group->ag_target = target;
753 		}
754 		ap = auth_portal_new(target->t_auth_group, $2);
755 		free($2);
756 		if (ap == NULL)
757 			return (1);
758 	}
759 	;
760 
761 target_portal_group:	PORTAL_GROUP STR STR
762 	{
763 		struct portal_group *tpg;
764 		struct auth_group *tag;
765 		struct port *tp;
766 
767 		tpg = portal_group_find(conf, $2);
768 		if (tpg == NULL) {
769 			log_warnx("unknown portal-group \"%s\" for target "
770 			    "\"%s\"", $2, target->t_name);
771 			free($2);
772 			free($3);
773 			return (1);
774 		}
775 		tag = auth_group_find(conf, $3);
776 		if (tag == NULL) {
777 			log_warnx("unknown auth-group \"%s\" for target "
778 			    "\"%s\"", $3, target->t_name);
779 			free($2);
780 			free($3);
781 			return (1);
782 		}
783 		tp = port_new(conf, target, tpg);
784 		if (tp == NULL) {
785 			log_warnx("can't link portal-group \"%s\" to target "
786 			    "\"%s\"", $2, target->t_name);
787 			free($2);
788 			return (1);
789 		}
790 		tp->p_auth_group = tag;
791 		free($2);
792 		free($3);
793 	}
794 	|		PORTAL_GROUP STR
795 	{
796 		struct portal_group *tpg;
797 		struct port *tp;
798 
799 		tpg = portal_group_find(conf, $2);
800 		if (tpg == NULL) {
801 			log_warnx("unknown portal-group \"%s\" for target "
802 			    "\"%s\"", $2, target->t_name);
803 			free($2);
804 			return (1);
805 		}
806 		tp = port_new(conf, target, tpg);
807 		if (tp == NULL) {
808 			log_warnx("can't link portal-group \"%s\" to target "
809 			    "\"%s\"", $2, target->t_name);
810 			free($2);
811 			return (1);
812 		}
813 		free($2);
814 	}
815 	;
816 
817 target_port:	PORT STR
818 	{
819 		struct pport *pp;
820 		struct port *tp;
821 		int ret, i_pp, i_vp = 0;
822 
823 		ret = sscanf($2, "ioctl/%d/%d", &i_pp, &i_vp);
824 		if (ret > 0) {
825 			tp = port_new_ioctl(conf, target, i_pp, i_vp);
826 			if (tp == NULL) {
827 				log_warnx("can't create new ioctl port for "
828 				    "target \"%s\"", target->t_name);
829 				free($2);
830 				return (1);
831 			}
832 		} else {
833 			pp = pport_find(conf, $2);
834 			if (pp == NULL) {
835 				log_warnx("unknown port \"%s\" for target \"%s\"",
836 				    $2, target->t_name);
837 				free($2);
838 				return (1);
839 			}
840 			if (!TAILQ_EMPTY(&pp->pp_ports)) {
841 				log_warnx("can't link port \"%s\" to target \"%s\", "
842 				    "port already linked to some target",
843 				    $2, target->t_name);
844 				free($2);
845 				return (1);
846 			}
847 			tp = port_new_pp(conf, target, pp);
848 			if (tp == NULL) {
849 				log_warnx("can't link port \"%s\" to target \"%s\"",
850 				    $2, target->t_name);
851 				free($2);
852 				return (1);
853 			}
854 		}
855 
856 		free($2);
857 	}
858 	;
859 
860 target_redirect:	REDIRECT STR
861 	{
862 		int error;
863 
864 		error = target_set_redirection(target, $2);
865 		free($2);
866 		if (error != 0)
867 			return (1);
868 	}
869 	;
870 
871 target_lun:	LUN lun_number
872     OPENING_BRACKET lun_entries CLOSING_BRACKET
873 	{
874 		lun = NULL;
875 	}
876 	;
877 
878 lun_number:	STR
879 	{
880 		uint64_t tmp;
881 		int ret;
882 		char *name;
883 
884 		if (expand_number($1, &tmp) != 0) {
885 			yyerror("invalid numeric value");
886 			free($1);
887 			return (1);
888 		}
889 		if (tmp >= MAX_LUNS) {
890 			yyerror("LU number is too big");
891 			free($1);
892 			return (1);
893 		}
894 
895 		ret = asprintf(&name, "%s,lun,%ju", target->t_name, tmp);
896 		if (ret <= 0)
897 			log_err(1, "asprintf");
898 		lun = lun_new(conf, name);
899 		if (lun == NULL)
900 			return (1);
901 
902 		lun_set_scsiname(lun, name);
903 		target->t_luns[tmp] = lun;
904 	}
905 	;
906 
907 target_lun_ref:	LUN STR STR
908 	{
909 		uint64_t tmp;
910 
911 		if (expand_number($2, &tmp) != 0) {
912 			yyerror("invalid numeric value");
913 			free($2);
914 			free($3);
915 			return (1);
916 		}
917 		free($2);
918 		if (tmp >= MAX_LUNS) {
919 			yyerror("LU number is too big");
920 			free($3);
921 			return (1);
922 		}
923 
924 		lun = lun_find(conf, $3);
925 		free($3);
926 		if (lun == NULL)
927 			return (1);
928 
929 		target->t_luns[tmp] = lun;
930 	}
931 	;
932 
933 lun_entries:
934 	|
935 	lun_entries lun_entry
936 	|
937 	lun_entries lun_entry SEMICOLON
938 	;
939 
940 lun_entry:
941 	lun_backend
942 	|
943 	lun_blocksize
944 	|
945 	lun_device_id
946 	|
947 	lun_device_type
948 	|
949 	lun_ctl_lun
950 	|
951 	lun_option
952 	|
953 	lun_path
954 	|
955 	lun_serial
956 	|
957 	lun_size
958 	;
959 
960 lun_backend:	BACKEND STR
961 	{
962 		if (lun->l_backend != NULL) {
963 			log_warnx("backend for lun \"%s\" "
964 			    "specified more than once",
965 			    lun->l_name);
966 			free($2);
967 			return (1);
968 		}
969 		lun_set_backend(lun, $2);
970 		free($2);
971 	}
972 	;
973 
974 lun_blocksize:	BLOCKSIZE STR
975 	{
976 		uint64_t tmp;
977 
978 		if (expand_number($2, &tmp) != 0) {
979 			yyerror("invalid numeric value");
980 			free($2);
981 			return (1);
982 		}
983 
984 		if (lun->l_blocksize != 0) {
985 			log_warnx("blocksize for lun \"%s\" "
986 			    "specified more than once",
987 			    lun->l_name);
988 			return (1);
989 		}
990 		lun_set_blocksize(lun, tmp);
991 	}
992 	;
993 
994 lun_device_id:	DEVICE_ID STR
995 	{
996 		if (lun->l_device_id != NULL) {
997 			log_warnx("device_id for lun \"%s\" "
998 			    "specified more than once",
999 			    lun->l_name);
1000 			free($2);
1001 			return (1);
1002 		}
1003 		lun_set_device_id(lun, $2);
1004 		free($2);
1005 	}
1006 	;
1007 
1008 lun_device_type:	DEVICE_TYPE STR
1009 	{
1010 		uint64_t tmp;
1011 
1012 		if (strcasecmp($2, "disk") == 0 ||
1013 		    strcasecmp($2, "direct") == 0)
1014 			tmp = 0;
1015 		else if (strcasecmp($2, "processor") == 0)
1016 			tmp = 3;
1017 		else if (strcasecmp($2, "cd") == 0 ||
1018 		    strcasecmp($2, "cdrom") == 0 ||
1019 		    strcasecmp($2, "dvd") == 0 ||
1020 		    strcasecmp($2, "dvdrom") == 0)
1021 			tmp = 5;
1022 		else if (expand_number($2, &tmp) != 0 ||
1023 		    tmp > 15) {
1024 			yyerror("invalid numeric value");
1025 			free($2);
1026 			return (1);
1027 		}
1028 
1029 		lun_set_device_type(lun, tmp);
1030 	}
1031 	;
1032 
1033 lun_ctl_lun:	CTL_LUN STR
1034 	{
1035 		uint64_t tmp;
1036 
1037 		if (expand_number($2, &tmp) != 0) {
1038 			yyerror("invalid numeric value");
1039 			free($2);
1040 			return (1);
1041 		}
1042 
1043 		if (lun->l_ctl_lun >= 0) {
1044 			log_warnx("ctl_lun for lun \"%s\" "
1045 			    "specified more than once",
1046 			    lun->l_name);
1047 			return (1);
1048 		}
1049 		lun_set_ctl_lun(lun, tmp);
1050 	}
1051 	;
1052 
1053 lun_option:	OPTION STR STR
1054 	{
1055 		struct option *o;
1056 
1057 		o = option_new(&lun->l_options, $2, $3);
1058 		free($2);
1059 		free($3);
1060 		if (o == NULL)
1061 			return (1);
1062 	}
1063 	;
1064 
1065 lun_path:	PATH STR
1066 	{
1067 		if (lun->l_path != NULL) {
1068 			log_warnx("path for lun \"%s\" "
1069 			    "specified more than once",
1070 			    lun->l_name);
1071 			free($2);
1072 			return (1);
1073 		}
1074 		lun_set_path(lun, $2);
1075 		free($2);
1076 	}
1077 	;
1078 
1079 lun_serial:	SERIAL STR
1080 	{
1081 		if (lun->l_serial != NULL) {
1082 			log_warnx("serial for lun \"%s\" "
1083 			    "specified more than once",
1084 			    lun->l_name);
1085 			free($2);
1086 			return (1);
1087 		}
1088 		lun_set_serial(lun, $2);
1089 		free($2);
1090 	}
1091 	;
1092 
1093 lun_size:	SIZE STR
1094 	{
1095 		uint64_t tmp;
1096 
1097 		if (expand_number($2, &tmp) != 0) {
1098 			yyerror("invalid numeric value");
1099 			free($2);
1100 			return (1);
1101 		}
1102 
1103 		if (lun->l_size != 0) {
1104 			log_warnx("size for lun \"%s\" "
1105 			    "specified more than once",
1106 			    lun->l_name);
1107 			return (1);
1108 		}
1109 		lun_set_size(lun, tmp);
1110 	}
1111 	;
1112 %%
1113 
1114 void
1115 yyerror(const char *str)
1116 {
1117 
1118 	log_warnx("error in configuration file at line %d near '%s': %s",
1119 	    lineno, yytext, str);
1120 }
1121 
1122 int
1123 parse_conf(struct conf *newconf, const char *path)
1124 {
1125 	int error;
1126 
1127 	conf = newconf;
1128 	yyin = fopen(path, "r");
1129 	if (yyin == NULL) {
1130 		log_warn("unable to open configuration file %s", path);
1131 		return (1);
1132 	}
1133 
1134 	lineno = 1;
1135 	yyrestart(yyin);
1136 	error = yyparse();
1137 	auth_group = NULL;
1138 	portal_group = NULL;
1139 	target = NULL;
1140 	lun = NULL;
1141 	fclose(yyin);
1142 
1143 	return (error);
1144 }
1145