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