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