xref: /freebsd/usr.sbin/ctld/parse.y (revision 91be33dc)
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_redirect
350 	|
351 	portal_group_tag
352 	;
353 
354 portal_group_discovery_auth_group:	DISCOVERY_AUTH_GROUP STR
355 	{
356 		if (portal_group->pg_discovery_auth_group != NULL) {
357 			log_warnx("discovery-auth-group for portal-group "
358 			    "\"%s\" specified more than once",
359 			    portal_group->pg_name);
360 			return (1);
361 		}
362 		portal_group->pg_discovery_auth_group =
363 		    auth_group_find(conf, $2);
364 		if (portal_group->pg_discovery_auth_group == NULL) {
365 			log_warnx("unknown discovery-auth-group \"%s\" "
366 			    "for portal-group \"%s\"",
367 			    $2, portal_group->pg_name);
368 			return (1);
369 		}
370 		free($2);
371 	}
372 	;
373 
374 portal_group_discovery_filter:	DISCOVERY_FILTER STR
375 	{
376 		int error;
377 
378 		error = portal_group_set_filter(portal_group, $2);
379 		free($2);
380 		if (error != 0)
381 			return (1);
382 	}
383 	;
384 
385 portal_group_foreign:	FOREIGN
386 	{
387 
388 		portal_group->pg_foreign = 1;
389 	}
390 	;
391 
392 portal_group_listen:	LISTEN STR
393 	{
394 		int error;
395 
396 		error = portal_group_add_listen(portal_group, $2, false);
397 		free($2);
398 		if (error != 0)
399 			return (1);
400 	}
401 	;
402 
403 portal_group_listen_iser:	LISTEN_ISER STR
404 	{
405 		int error;
406 
407 		error = portal_group_add_listen(portal_group, $2, true);
408 		free($2);
409 		if (error != 0)
410 			return (1);
411 	}
412 	;
413 
414 portal_group_offload:	OFFLOAD STR
415 	{
416 		int error;
417 
418 		error = portal_group_set_offload(portal_group, $2);
419 		free($2);
420 		if (error != 0)
421 			return (1);
422 	}
423 	;
424 
425 portal_group_redirect:	REDIRECT STR
426 	{
427 		int error;
428 
429 		error = portal_group_set_redirection(portal_group, $2);
430 		free($2);
431 		if (error != 0)
432 			return (1);
433 	}
434 	;
435 
436 portal_group_tag:	TAG STR
437 	{
438 		uint64_t tmp;
439 
440 		if (expand_number($2, &tmp) != 0) {
441 			yyerror("invalid numeric value");
442 			free($2);
443 			return (1);
444 		}
445 
446 		portal_group->pg_tag = tmp;
447 	}
448 	;
449 
450 lun:	LUN lun_name
451     OPENING_BRACKET lun_entries CLOSING_BRACKET
452 	{
453 		lun = NULL;
454 	}
455 	;
456 
457 lun_name:	STR
458 	{
459 		lun = lun_new(conf, $1);
460 		free($1);
461 		if (lun == NULL)
462 			return (1);
463 	}
464 	;
465 
466 target:	TARGET target_name
467     OPENING_BRACKET target_entries CLOSING_BRACKET
468 	{
469 		target = NULL;
470 	}
471 	;
472 
473 target_name:	STR
474 	{
475 		target = target_new(conf, $1);
476 		free($1);
477 		if (target == NULL)
478 			return (1);
479 	}
480 	;
481 
482 target_entries:
483 	|
484 	target_entries target_entry
485 	|
486 	target_entries target_entry SEMICOLON
487 	;
488 
489 target_entry:
490 	target_alias
491 	|
492 	target_auth_group
493 	|
494 	target_auth_type
495 	|
496 	target_chap
497 	|
498 	target_chap_mutual
499 	|
500 	target_initiator_name
501 	|
502 	target_initiator_portal
503 	|
504 	target_portal_group
505 	|
506 	target_port
507 	|
508 	target_redirect
509 	|
510 	target_lun
511 	|
512 	target_lun_ref
513 	;
514 
515 target_alias:	ALIAS STR
516 	{
517 		if (target->t_alias != NULL) {
518 			log_warnx("alias for target \"%s\" "
519 			    "specified more than once", target->t_name);
520 			return (1);
521 		}
522 		target->t_alias = $2;
523 	}
524 	;
525 
526 target_auth_group:	AUTH_GROUP STR
527 	{
528 		if (target->t_auth_group != NULL) {
529 			if (target->t_auth_group->ag_name != NULL)
530 				log_warnx("auth-group for target \"%s\" "
531 				    "specified more than once", target->t_name);
532 			else
533 				log_warnx("cannot use both auth-group and explicit "
534 				    "authorisations for target \"%s\"",
535 				    target->t_name);
536 			return (1);
537 		}
538 		target->t_auth_group = auth_group_find(conf, $2);
539 		if (target->t_auth_group == NULL) {
540 			log_warnx("unknown auth-group \"%s\" for target "
541 			    "\"%s\"", $2, target->t_name);
542 			return (1);
543 		}
544 		free($2);
545 	}
546 	;
547 
548 target_auth_type:	AUTH_TYPE STR
549 	{
550 		int error;
551 
552 		if (target->t_auth_group != NULL) {
553 			if (target->t_auth_group->ag_name != NULL) {
554 				log_warnx("cannot use both auth-group and "
555 				    "auth-type for target \"%s\"",
556 				    target->t_name);
557 				return (1);
558 			}
559 		} else {
560 			target->t_auth_group = auth_group_new(conf, NULL);
561 			if (target->t_auth_group == NULL) {
562 				free($2);
563 				return (1);
564 			}
565 			target->t_auth_group->ag_target = target;
566 		}
567 		error = auth_group_set_type(target->t_auth_group, $2);
568 		free($2);
569 		if (error != 0)
570 			return (1);
571 	}
572 	;
573 
574 target_chap:	CHAP STR STR
575 	{
576 		const struct auth *ca;
577 
578 		if (target->t_auth_group != NULL) {
579 			if (target->t_auth_group->ag_name != NULL) {
580 				log_warnx("cannot use both auth-group and "
581 				    "chap for target \"%s\"",
582 				    target->t_name);
583 				free($2);
584 				free($3);
585 				return (1);
586 			}
587 		} else {
588 			target->t_auth_group = auth_group_new(conf, NULL);
589 			if (target->t_auth_group == NULL) {
590 				free($2);
591 				free($3);
592 				return (1);
593 			}
594 			target->t_auth_group->ag_target = target;
595 		}
596 		ca = auth_new_chap(target->t_auth_group, $2, $3);
597 		free($2);
598 		free($3);
599 		if (ca == NULL)
600 			return (1);
601 	}
602 	;
603 
604 target_chap_mutual:	CHAP_MUTUAL STR STR STR STR
605 	{
606 		const struct auth *ca;
607 
608 		if (target->t_auth_group != NULL) {
609 			if (target->t_auth_group->ag_name != NULL) {
610 				log_warnx("cannot use both auth-group and "
611 				    "chap-mutual for target \"%s\"",
612 				    target->t_name);
613 				free($2);
614 				free($3);
615 				free($4);
616 				free($5);
617 				return (1);
618 			}
619 		} else {
620 			target->t_auth_group = auth_group_new(conf, NULL);
621 			if (target->t_auth_group == NULL) {
622 				free($2);
623 				free($3);
624 				free($4);
625 				free($5);
626 				return (1);
627 			}
628 			target->t_auth_group->ag_target = target;
629 		}
630 		ca = auth_new_chap_mutual(target->t_auth_group,
631 		    $2, $3, $4, $5);
632 		free($2);
633 		free($3);
634 		free($4);
635 		free($5);
636 		if (ca == NULL)
637 			return (1);
638 	}
639 	;
640 
641 target_initiator_name:	INITIATOR_NAME STR
642 	{
643 		const struct auth_name *an;
644 
645 		if (target->t_auth_group != NULL) {
646 			if (target->t_auth_group->ag_name != NULL) {
647 				log_warnx("cannot use both auth-group and "
648 				    "initiator-name for target \"%s\"",
649 				    target->t_name);
650 				free($2);
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 				return (1);
658 			}
659 			target->t_auth_group->ag_target = target;
660 		}
661 		an = auth_name_new(target->t_auth_group, $2);
662 		free($2);
663 		if (an == NULL)
664 			return (1);
665 	}
666 	;
667 
668 target_initiator_portal:	INITIATOR_PORTAL STR
669 	{
670 		const struct auth_portal *ap;
671 
672 		if (target->t_auth_group != NULL) {
673 			if (target->t_auth_group->ag_name != NULL) {
674 				log_warnx("cannot use both auth-group and "
675 				    "initiator-portal for target \"%s\"",
676 				    target->t_name);
677 				free($2);
678 				return (1);
679 			}
680 		} else {
681 			target->t_auth_group = auth_group_new(conf, NULL);
682 			if (target->t_auth_group == NULL) {
683 				free($2);
684 				return (1);
685 			}
686 			target->t_auth_group->ag_target = target;
687 		}
688 		ap = auth_portal_new(target->t_auth_group, $2);
689 		free($2);
690 		if (ap == NULL)
691 			return (1);
692 	}
693 	;
694 
695 target_portal_group:	PORTAL_GROUP STR STR
696 	{
697 		struct portal_group *tpg;
698 		struct auth_group *tag;
699 		struct port *tp;
700 
701 		tpg = portal_group_find(conf, $2);
702 		if (tpg == NULL) {
703 			log_warnx("unknown portal-group \"%s\" for target "
704 			    "\"%s\"", $2, target->t_name);
705 			free($2);
706 			free($3);
707 			return (1);
708 		}
709 		tag = auth_group_find(conf, $3);
710 		if (tag == NULL) {
711 			log_warnx("unknown auth-group \"%s\" for target "
712 			    "\"%s\"", $3, target->t_name);
713 			free($2);
714 			free($3);
715 			return (1);
716 		}
717 		tp = port_new(conf, target, tpg);
718 		if (tp == NULL) {
719 			log_warnx("can't link portal-group \"%s\" to target "
720 			    "\"%s\"", $2, target->t_name);
721 			free($2);
722 			return (1);
723 		}
724 		tp->p_auth_group = tag;
725 		free($2);
726 		free($3);
727 	}
728 	|		PORTAL_GROUP STR
729 	{
730 		struct portal_group *tpg;
731 		struct port *tp;
732 
733 		tpg = portal_group_find(conf, $2);
734 		if (tpg == NULL) {
735 			log_warnx("unknown portal-group \"%s\" for target "
736 			    "\"%s\"", $2, target->t_name);
737 			free($2);
738 			return (1);
739 		}
740 		tp = port_new(conf, target, tpg);
741 		if (tp == NULL) {
742 			log_warnx("can't link portal-group \"%s\" to target "
743 			    "\"%s\"", $2, target->t_name);
744 			free($2);
745 			return (1);
746 		}
747 		free($2);
748 	}
749 	;
750 
751 target_port:	PORT STR
752 	{
753 		struct pport *pp;
754 		struct port *tp;
755 
756 		pp = pport_find(conf, $2);
757 		if (pp == NULL) {
758 			log_warnx("unknown port \"%s\" for target \"%s\"",
759 			    $2, target->t_name);
760 			free($2);
761 			return (1);
762 		}
763 		if (!TAILQ_EMPTY(&pp->pp_ports)) {
764 			log_warnx("can't link port \"%s\" to target \"%s\", "
765 			    "port already linked to some target",
766 			    $2, target->t_name);
767 			free($2);
768 			return (1);
769 		}
770 		tp = port_new_pp(conf, target, pp);
771 		if (tp == NULL) {
772 			log_warnx("can't link port \"%s\" to target \"%s\"",
773 			    $2, target->t_name);
774 			free($2);
775 			return (1);
776 		}
777 		free($2);
778 	}
779 	;
780 
781 target_redirect:	REDIRECT STR
782 	{
783 		int error;
784 
785 		error = target_set_redirection(target, $2);
786 		free($2);
787 		if (error != 0)
788 			return (1);
789 	}
790 	;
791 
792 target_lun:	LUN lun_number
793     OPENING_BRACKET lun_entries CLOSING_BRACKET
794 	{
795 		lun = NULL;
796 	}
797 	;
798 
799 lun_number:	STR
800 	{
801 		uint64_t tmp;
802 		int ret;
803 		char *name;
804 
805 		if (expand_number($1, &tmp) != 0) {
806 			yyerror("invalid numeric value");
807 			free($1);
808 			return (1);
809 		}
810 
811 		ret = asprintf(&name, "%s,lun,%ju", target->t_name, tmp);
812 		if (ret <= 0)
813 			log_err(1, "asprintf");
814 		lun = lun_new(conf, name);
815 		if (lun == NULL)
816 			return (1);
817 
818 		lun_set_scsiname(lun, name);
819 		target->t_luns[tmp] = lun;
820 	}
821 	;
822 
823 target_lun_ref:	LUN STR STR
824 	{
825 		uint64_t tmp;
826 
827 		if (expand_number($2, &tmp) != 0) {
828 			yyerror("invalid numeric value");
829 			free($2);
830 			free($3);
831 			return (1);
832 		}
833 		free($2);
834 
835 		lun = lun_find(conf, $3);
836 		free($3);
837 		if (lun == NULL)
838 			return (1);
839 
840 		target->t_luns[tmp] = lun;
841 	}
842 	;
843 
844 lun_entries:
845 	|
846 	lun_entries lun_entry
847 	|
848 	lun_entries lun_entry SEMICOLON
849 	;
850 
851 lun_entry:
852 	lun_backend
853 	|
854 	lun_blocksize
855 	|
856 	lun_device_id
857 	|
858 	lun_device_type
859 	|
860 	lun_ctl_lun
861 	|
862 	lun_option
863 	|
864 	lun_path
865 	|
866 	lun_serial
867 	|
868 	lun_size
869 	;
870 
871 lun_backend:	BACKEND STR
872 	{
873 		if (lun->l_backend != NULL) {
874 			log_warnx("backend for lun \"%s\" "
875 			    "specified more than once",
876 			    lun->l_name);
877 			free($2);
878 			return (1);
879 		}
880 		lun_set_backend(lun, $2);
881 		free($2);
882 	}
883 	;
884 
885 lun_blocksize:	BLOCKSIZE STR
886 	{
887 		uint64_t tmp;
888 
889 		if (expand_number($2, &tmp) != 0) {
890 			yyerror("invalid numeric value");
891 			free($2);
892 			return (1);
893 		}
894 
895 		if (lun->l_blocksize != 0) {
896 			log_warnx("blocksize for lun \"%s\" "
897 			    "specified more than once",
898 			    lun->l_name);
899 			return (1);
900 		}
901 		lun_set_blocksize(lun, tmp);
902 	}
903 	;
904 
905 lun_device_id:	DEVICE_ID STR
906 	{
907 		if (lun->l_device_id != NULL) {
908 			log_warnx("device_id for lun \"%s\" "
909 			    "specified more than once",
910 			    lun->l_name);
911 			free($2);
912 			return (1);
913 		}
914 		lun_set_device_id(lun, $2);
915 		free($2);
916 	}
917 	;
918 
919 lun_device_type:	DEVICE_TYPE STR
920 	{
921 		uint64_t tmp;
922 
923 		if (strcasecmp($2, "disk") == 0 ||
924 		    strcasecmp($2, "direct") == 0)
925 			tmp = 0;
926 		else if (strcasecmp($2, "processor") == 0)
927 			tmp = 3;
928 		else if (strcasecmp($2, "cd") == 0 ||
929 		    strcasecmp($2, "cdrom") == 0 ||
930 		    strcasecmp($2, "dvd") == 0 ||
931 		    strcasecmp($2, "dvdrom") == 0)
932 			tmp = 5;
933 		else if (expand_number($2, &tmp) != 0 ||
934 		    tmp > 15) {
935 			yyerror("invalid numeric value");
936 			free($2);
937 			return (1);
938 		}
939 
940 		lun_set_device_type(lun, tmp);
941 	}
942 	;
943 
944 lun_ctl_lun:	CTL_LUN STR
945 	{
946 		uint64_t tmp;
947 
948 		if (expand_number($2, &tmp) != 0) {
949 			yyerror("invalid numeric value");
950 			free($2);
951 			return (1);
952 		}
953 
954 		if (lun->l_ctl_lun >= 0) {
955 			log_warnx("ctl_lun for lun \"%s\" "
956 			    "specified more than once",
957 			    lun->l_name);
958 			return (1);
959 		}
960 		lun_set_ctl_lun(lun, tmp);
961 	}
962 	;
963 
964 lun_option:	OPTION STR STR
965 	{
966 		struct lun_option *clo;
967 
968 		clo = lun_option_new(lun, $2, $3);
969 		free($2);
970 		free($3);
971 		if (clo == NULL)
972 			return (1);
973 	}
974 	;
975 
976 lun_path:	PATH STR
977 	{
978 		if (lun->l_path != NULL) {
979 			log_warnx("path for lun \"%s\" "
980 			    "specified more than once",
981 			    lun->l_name);
982 			free($2);
983 			return (1);
984 		}
985 		lun_set_path(lun, $2);
986 		free($2);
987 	}
988 	;
989 
990 lun_serial:	SERIAL STR
991 	{
992 		if (lun->l_serial != NULL) {
993 			log_warnx("serial for lun \"%s\" "
994 			    "specified more than once",
995 			    lun->l_name);
996 			free($2);
997 			return (1);
998 		}
999 		lun_set_serial(lun, $2);
1000 		free($2);
1001 	}
1002 	;
1003 
1004 lun_size:	SIZE STR
1005 	{
1006 		uint64_t tmp;
1007 
1008 		if (expand_number($2, &tmp) != 0) {
1009 			yyerror("invalid numeric value");
1010 			free($2);
1011 			return (1);
1012 		}
1013 
1014 		if (lun->l_size != 0) {
1015 			log_warnx("size for lun \"%s\" "
1016 			    "specified more than once",
1017 			    lun->l_name);
1018 			return (1);
1019 		}
1020 		lun_set_size(lun, tmp);
1021 	}
1022 	;
1023 %%
1024 
1025 void
1026 yyerror(const char *str)
1027 {
1028 
1029 	log_warnx("error in configuration file at line %d near '%s': %s",
1030 	    lineno, yytext, str);
1031 }
1032 
1033 static void
1034 check_perms(const char *path)
1035 {
1036 	struct stat sb;
1037 	int error;
1038 
1039 	error = stat(path, &sb);
1040 	if (error != 0) {
1041 		log_warn("stat");
1042 		return;
1043 	}
1044 	if (sb.st_mode & S_IWOTH) {
1045 		log_warnx("%s is world-writable", path);
1046 	} else if (sb.st_mode & S_IROTH) {
1047 		log_warnx("%s is world-readable", path);
1048 	} else if (sb.st_mode & S_IXOTH) {
1049 		/*
1050 		 * Ok, this one doesn't matter, but still do it,
1051 		 * just for consistency.
1052 		 */
1053 		log_warnx("%s is world-executable", path);
1054 	}
1055 
1056 	/*
1057 	 * XXX: Should we also check for owner != 0?
1058 	 */
1059 }
1060 
1061 struct conf *
1062 conf_new_from_file(const char *path, struct conf *oldconf)
1063 {
1064 	struct auth_group *ag;
1065 	struct portal_group *pg;
1066 	struct pport *pp;
1067 	int error;
1068 
1069 	log_debugx("obtaining configuration from %s", path);
1070 
1071 	conf = conf_new();
1072 
1073 	TAILQ_FOREACH(pp, &oldconf->conf_pports, pp_next)
1074 		pport_copy(pp, conf);
1075 
1076 	ag = auth_group_new(conf, "default");
1077 	assert(ag != NULL);
1078 
1079 	ag = auth_group_new(conf, "no-authentication");
1080 	assert(ag != NULL);
1081 	ag->ag_type = AG_TYPE_NO_AUTHENTICATION;
1082 
1083 	ag = auth_group_new(conf, "no-access");
1084 	assert(ag != NULL);
1085 	ag->ag_type = AG_TYPE_DENY;
1086 
1087 	pg = portal_group_new(conf, "default");
1088 	assert(pg != NULL);
1089 
1090 	yyin = fopen(path, "r");
1091 	if (yyin == NULL) {
1092 		log_warn("unable to open configuration file %s", path);
1093 		conf_delete(conf);
1094 		return (NULL);
1095 	}
1096 	check_perms(path);
1097 	lineno = 1;
1098 	yyrestart(yyin);
1099 	error = yyparse();
1100 	auth_group = NULL;
1101 	portal_group = NULL;
1102 	target = NULL;
1103 	lun = NULL;
1104 	fclose(yyin);
1105 	if (error != 0) {
1106 		conf_delete(conf);
1107 		return (NULL);
1108 	}
1109 
1110 	if (conf->conf_default_ag_defined == false) {
1111 		log_debugx("auth-group \"default\" not defined; "
1112 		    "going with defaults");
1113 		ag = auth_group_find(conf, "default");
1114 		assert(ag != NULL);
1115 		ag->ag_type = AG_TYPE_DENY;
1116 	}
1117 
1118 	if (conf->conf_default_pg_defined == false) {
1119 		log_debugx("portal-group \"default\" not defined; "
1120 		    "going with defaults");
1121 		pg = portal_group_find(conf, "default");
1122 		assert(pg != NULL);
1123 		portal_group_add_listen(pg, "0.0.0.0:3260", false);
1124 		portal_group_add_listen(pg, "[::]:3260", false);
1125 	}
1126 
1127 	conf->conf_kernel_port_on = true;
1128 
1129 	error = conf_verify(conf);
1130 	if (error != 0) {
1131 		conf_delete(conf);
1132 		return (NULL);
1133 	}
1134 
1135 	return (conf);
1136 }
1137