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