parse.y revision 278354
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: head/usr.sbin/ctld/parse.y 278354 2015-02-07 13:19:04Z mav $
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
44extern FILE *yyin;
45extern char *yytext;
46extern int lineno;
47
48static struct conf *conf = NULL;
49static struct auth_group *auth_group = NULL;
50static struct portal_group *portal_group = NULL;
51static struct target *target = NULL;
52static struct lun *lun = NULL;
53
54extern void	yyerror(const char *);
55extern int	yylex(void);
56extern 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
76statements:
77	|
78	statements statement
79	|
80	statements statement SEMICOLON
81	;
82
83statement:
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
107debug:		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
121timeout:	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
135maxproc:	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
149pidfile:	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
160isns_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
171isns_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
185isns_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
199auth_group:	AUTH_GROUP auth_group_name
200    OPENING_BRACKET auth_group_entries CLOSING_BRACKET
201	{
202		auth_group = NULL;
203	}
204	;
205
206auth_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
225auth_group_entries:
226	|
227	auth_group_entries auth_group_entry
228	|
229	auth_group_entries auth_group_entry SEMICOLON
230	;
231
232auth_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
244auth_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
255auth_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
267auth_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
281auth_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
292auth_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
303portal_group:	PORTAL_GROUP portal_group_name
304    OPENING_BRACKET portal_group_entries CLOSING_BRACKET
305	{
306		portal_group = NULL;
307	}
308	;
309
310portal_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
329portal_group_entries:
330	|
331	portal_group_entries portal_group_entry
332	|
333	portal_group_entries portal_group_entry SEMICOLON
334	;
335
336portal_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_redirect
346	;
347
348portal_group_discovery_auth_group:	DISCOVERY_AUTH_GROUP STR
349	{
350		if (portal_group->pg_discovery_auth_group != NULL) {
351			log_warnx("discovery-auth-group for portal-group "
352			    "\"%s\" specified more than once",
353			    portal_group->pg_name);
354			return (1);
355		}
356		portal_group->pg_discovery_auth_group =
357		    auth_group_find(conf, $2);
358		if (portal_group->pg_discovery_auth_group == NULL) {
359			log_warnx("unknown discovery-auth-group \"%s\" "
360			    "for portal-group \"%s\"",
361			    $2, portal_group->pg_name);
362			return (1);
363		}
364		free($2);
365	}
366	;
367
368portal_group_discovery_filter:	DISCOVERY_FILTER STR
369	{
370		int error;
371
372		error = portal_group_set_filter(portal_group, $2);
373		free($2);
374		if (error != 0)
375			return (1);
376	}
377	;
378
379portal_group_listen:	LISTEN STR
380	{
381		int error;
382
383		error = portal_group_add_listen(portal_group, $2, false);
384		free($2);
385		if (error != 0)
386			return (1);
387	}
388	;
389
390portal_group_listen_iser:	LISTEN_ISER STR
391	{
392		int error;
393
394		error = portal_group_add_listen(portal_group, $2, true);
395		free($2);
396		if (error != 0)
397			return (1);
398	}
399	;
400
401portal_group_redirect:	REDIRECT STR
402	{
403		int error;
404
405		error = portal_group_set_redirection(portal_group, $2);
406		free($2);
407		if (error != 0)
408			return (1);
409	}
410	;
411
412lun:	LUN lun_name
413    OPENING_BRACKET lun_entries CLOSING_BRACKET
414	{
415		lun = NULL;
416	}
417	;
418
419lun_name:	STR
420	{
421		lun = lun_new(conf, $1);
422		free($1);
423		if (lun == NULL)
424			return (1);
425	}
426	;
427
428target:	TARGET target_name
429    OPENING_BRACKET target_entries CLOSING_BRACKET
430	{
431		target = NULL;
432	}
433	;
434
435target_name:	STR
436	{
437		target = target_new(conf, $1);
438		free($1);
439		if (target == NULL)
440			return (1);
441	}
442	;
443
444target_entries:
445	|
446	target_entries target_entry
447	|
448	target_entries target_entry SEMICOLON
449	;
450
451target_entry:
452	target_alias
453	|
454	target_auth_group
455	|
456	target_auth_type
457	|
458	target_chap
459	|
460	target_chap_mutual
461	|
462	target_initiator_name
463	|
464	target_initiator_portal
465	|
466	target_offload
467	|
468	target_portal_group
469	|
470	target_port
471	|
472	target_redirect
473	|
474	target_lun
475	|
476	target_lun_ref
477	;
478
479target_alias:	ALIAS STR
480	{
481		if (target->t_alias != NULL) {
482			log_warnx("alias for target \"%s\" "
483			    "specified more than once", target->t_name);
484			return (1);
485		}
486		target->t_alias = $2;
487	}
488	;
489
490target_auth_group:	AUTH_GROUP STR
491	{
492		if (target->t_auth_group != NULL) {
493			if (target->t_auth_group->ag_name != NULL)
494				log_warnx("auth-group for target \"%s\" "
495				    "specified more than once", target->t_name);
496			else
497				log_warnx("cannot use both auth-group and explicit "
498				    "authorisations for target \"%s\"",
499				    target->t_name);
500			return (1);
501		}
502		target->t_auth_group = auth_group_find(conf, $2);
503		if (target->t_auth_group == NULL) {
504			log_warnx("unknown auth-group \"%s\" for target "
505			    "\"%s\"", $2, target->t_name);
506			return (1);
507		}
508		free($2);
509	}
510	;
511
512target_auth_type:	AUTH_TYPE STR
513	{
514		int error;
515
516		if (target->t_auth_group != NULL) {
517			if (target->t_auth_group->ag_name != NULL) {
518				log_warnx("cannot use both auth-group and "
519				    "auth-type for target \"%s\"",
520				    target->t_name);
521				return (1);
522			}
523		} else {
524			target->t_auth_group = auth_group_new(conf, NULL);
525			if (target->t_auth_group == NULL) {
526				free($2);
527				return (1);
528			}
529			target->t_auth_group->ag_target = target;
530		}
531		error = auth_group_set_type(target->t_auth_group, $2);
532		free($2);
533		if (error != 0)
534			return (1);
535	}
536	;
537
538target_chap:	CHAP STR STR
539	{
540		const struct auth *ca;
541
542		if (target->t_auth_group != NULL) {
543			if (target->t_auth_group->ag_name != NULL) {
544				log_warnx("cannot use both auth-group and "
545				    "chap for target \"%s\"",
546				    target->t_name);
547				free($2);
548				free($3);
549				return (1);
550			}
551		} else {
552			target->t_auth_group = auth_group_new(conf, NULL);
553			if (target->t_auth_group == NULL) {
554				free($2);
555				free($3);
556				return (1);
557			}
558			target->t_auth_group->ag_target = target;
559		}
560		ca = auth_new_chap(target->t_auth_group, $2, $3);
561		free($2);
562		free($3);
563		if (ca == NULL)
564			return (1);
565	}
566	;
567
568target_chap_mutual:	CHAP_MUTUAL STR STR STR STR
569	{
570		const struct auth *ca;
571
572		if (target->t_auth_group != NULL) {
573			if (target->t_auth_group->ag_name != NULL) {
574				log_warnx("cannot use both auth-group and "
575				    "chap-mutual for target \"%s\"",
576				    target->t_name);
577				free($2);
578				free($3);
579				free($4);
580				free($5);
581				return (1);
582			}
583		} else {
584			target->t_auth_group = auth_group_new(conf, NULL);
585			if (target->t_auth_group == NULL) {
586				free($2);
587				free($3);
588				free($4);
589				free($5);
590				return (1);
591			}
592			target->t_auth_group->ag_target = target;
593		}
594		ca = auth_new_chap_mutual(target->t_auth_group,
595		    $2, $3, $4, $5);
596		free($2);
597		free($3);
598		free($4);
599		free($5);
600		if (ca == NULL)
601			return (1);
602	}
603	;
604
605target_initiator_name:	INITIATOR_NAME STR
606	{
607		const struct auth_name *an;
608
609		if (target->t_auth_group != NULL) {
610			if (target->t_auth_group->ag_name != NULL) {
611				log_warnx("cannot use both auth-group and "
612				    "initiator-name for target \"%s\"",
613				    target->t_name);
614				free($2);
615				return (1);
616			}
617		} else {
618			target->t_auth_group = auth_group_new(conf, NULL);
619			if (target->t_auth_group == NULL) {
620				free($2);
621				return (1);
622			}
623			target->t_auth_group->ag_target = target;
624		}
625		an = auth_name_new(target->t_auth_group, $2);
626		free($2);
627		if (an == NULL)
628			return (1);
629	}
630	;
631
632target_initiator_portal:	INITIATOR_PORTAL STR
633	{
634		const struct auth_portal *ap;
635
636		if (target->t_auth_group != NULL) {
637			if (target->t_auth_group->ag_name != NULL) {
638				log_warnx("cannot use both auth-group and "
639				    "initiator-portal for target \"%s\"",
640				    target->t_name);
641				free($2);
642				return (1);
643			}
644		} else {
645			target->t_auth_group = auth_group_new(conf, NULL);
646			if (target->t_auth_group == NULL) {
647				free($2);
648				return (1);
649			}
650			target->t_auth_group->ag_target = target;
651		}
652		ap = auth_portal_new(target->t_auth_group, $2);
653		free($2);
654		if (ap == NULL)
655			return (1);
656	}
657	;
658
659target_offload:	OFFLOAD STR
660	{
661		int error;
662
663		error = target_set_offload(target, $2);
664		free($2);
665		if (error != 0)
666			return (1);
667	}
668	;
669
670target_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
726target_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
756target_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
767target_lun:	LUN lun_number
768    OPENING_BRACKET lun_entries CLOSING_BRACKET
769	{
770		lun = NULL;
771	}
772	;
773
774lun_number:	STR
775	{
776		uint64_t tmp;
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		asprintf(&name, "%s,lun,%ju", target->t_name, tmp);
786		lun = lun_new(conf, name);
787		if (lun == NULL)
788			return (1);
789
790		lun_set_scsiname(lun, name);
791		target->t_luns[tmp] = lun;
792	}
793	;
794
795target_lun_ref:	LUN STR STR
796	{
797		uint64_t tmp;
798
799		if (expand_number($2, &tmp) != 0) {
800			yyerror("invalid numeric value");
801			free($2);
802			free($3);
803			return (1);
804		}
805		free($2);
806
807		lun = lun_find(conf, $3);
808		free($3);
809		if (lun == NULL)
810			return (1);
811
812		target->t_luns[tmp] = lun;
813	}
814	;
815
816lun_entries:
817	|
818	lun_entries lun_entry
819	|
820	lun_entries lun_entry SEMICOLON
821	;
822
823lun_entry:
824	lun_backend
825	|
826	lun_blocksize
827	|
828	lun_device_id
829	|
830	lun_option
831	|
832	lun_path
833	|
834	lun_serial
835	|
836	lun_size
837	;
838
839lun_backend:	BACKEND STR
840	{
841		if (lun->l_backend != NULL) {
842			log_warnx("backend for lun \"%s\" "
843			    "specified more than once",
844			    lun->l_name);
845			free($2);
846			return (1);
847		}
848		lun_set_backend(lun, $2);
849		free($2);
850	}
851	;
852
853lun_blocksize:	BLOCKSIZE STR
854	{
855		uint64_t tmp;
856
857		if (expand_number($2, &tmp) != 0) {
858			yyerror("invalid numeric value");
859			free($2);
860			return (1);
861		}
862
863		if (lun->l_blocksize != 0) {
864			log_warnx("blocksize for lun \"%s\" "
865			    "specified more than once",
866			    lun->l_name);
867			return (1);
868		}
869		lun_set_blocksize(lun, tmp);
870	}
871	;
872
873lun_device_id:	DEVICE_ID STR
874	{
875		if (lun->l_device_id != NULL) {
876			log_warnx("device_id for lun \"%s\" "
877			    "specified more than once",
878			    lun->l_name);
879			free($2);
880			return (1);
881		}
882		lun_set_device_id(lun, $2);
883		free($2);
884	}
885	;
886
887lun_option:	OPTION STR STR
888	{
889		struct lun_option *clo;
890
891		clo = lun_option_new(lun, $2, $3);
892		free($2);
893		free($3);
894		if (clo == NULL)
895			return (1);
896	}
897	;
898
899lun_path:	PATH STR
900	{
901		if (lun->l_path != NULL) {
902			log_warnx("path for lun \"%s\" "
903			    "specified more than once",
904			    lun->l_name);
905			free($2);
906			return (1);
907		}
908		lun_set_path(lun, $2);
909		free($2);
910	}
911	;
912
913lun_serial:	SERIAL STR
914	{
915		if (lun->l_serial != NULL) {
916			log_warnx("serial for lun \"%s\" "
917			    "specified more than once",
918			    lun->l_name);
919			free($2);
920			return (1);
921		}
922		lun_set_serial(lun, $2);
923		free($2);
924	}
925	;
926
927lun_size:	SIZE STR
928	{
929		uint64_t tmp;
930
931		if (expand_number($2, &tmp) != 0) {
932			yyerror("invalid numeric value");
933			free($2);
934			return (1);
935		}
936
937		if (lun->l_size != 0) {
938			log_warnx("size for lun \"%s\" "
939			    "specified more than once",
940			    lun->l_name);
941			return (1);
942		}
943		lun_set_size(lun, tmp);
944	}
945	;
946%%
947
948void
949yyerror(const char *str)
950{
951
952	log_warnx("error in configuration file at line %d near '%s': %s",
953	    lineno, yytext, str);
954}
955
956static void
957check_perms(const char *path)
958{
959	struct stat sb;
960	int error;
961
962	error = stat(path, &sb);
963	if (error != 0) {
964		log_warn("stat");
965		return;
966	}
967	if (sb.st_mode & S_IWOTH) {
968		log_warnx("%s is world-writable", path);
969	} else if (sb.st_mode & S_IROTH) {
970		log_warnx("%s is world-readable", path);
971	} else if (sb.st_mode & S_IXOTH) {
972		/*
973		 * Ok, this one doesn't matter, but still do it,
974		 * just for consistency.
975		 */
976		log_warnx("%s is world-executable", path);
977	}
978
979	/*
980	 * XXX: Should we also check for owner != 0?
981	 */
982}
983
984struct conf *
985conf_new_from_file(const char *path, struct conf *oldconf)
986{
987	struct auth_group *ag;
988	struct portal_group *pg;
989	struct pport *pp;
990	int error;
991
992	log_debugx("obtaining configuration from %s", path);
993
994	conf = conf_new();
995
996	TAILQ_FOREACH(pp, &oldconf->conf_pports, pp_next)
997		pport_copy(pp, conf);
998
999	ag = auth_group_new(conf, "default");
1000	assert(ag != NULL);
1001
1002	ag = auth_group_new(conf, "no-authentication");
1003	assert(ag != NULL);
1004	ag->ag_type = AG_TYPE_NO_AUTHENTICATION;
1005
1006	ag = auth_group_new(conf, "no-access");
1007	assert(ag != NULL);
1008	ag->ag_type = AG_TYPE_DENY;
1009
1010	pg = portal_group_new(conf, "default");
1011	assert(pg != NULL);
1012
1013	yyin = fopen(path, "r");
1014	if (yyin == NULL) {
1015		log_warn("unable to open configuration file %s", path);
1016		conf_delete(conf);
1017		return (NULL);
1018	}
1019	check_perms(path);
1020	lineno = 1;
1021	yyrestart(yyin);
1022	error = yyparse();
1023	auth_group = NULL;
1024	portal_group = NULL;
1025	target = NULL;
1026	lun = NULL;
1027	fclose(yyin);
1028	if (error != 0) {
1029		conf_delete(conf);
1030		return (NULL);
1031	}
1032
1033	if (conf->conf_default_ag_defined == false) {
1034		log_debugx("auth-group \"default\" not defined; "
1035		    "going with defaults");
1036		ag = auth_group_find(conf, "default");
1037		assert(ag != NULL);
1038		ag->ag_type = AG_TYPE_DENY;
1039	}
1040
1041	if (conf->conf_default_pg_defined == false) {
1042		log_debugx("portal-group \"default\" not defined; "
1043		    "going with defaults");
1044		pg = portal_group_find(conf, "default");
1045		assert(pg != NULL);
1046		portal_group_add_listen(pg, "0.0.0.0:3260", false);
1047		portal_group_add_listen(pg, "[::]:3260", false);
1048	}
1049
1050	conf->conf_kernel_port_on = true;
1051
1052	error = conf_verify(conf);
1053	if (error != 0) {
1054		conf_delete(conf);
1055		return (NULL);
1056	}
1057
1058	return (conf);
1059}
1060