1%{
2/*-
3 * SPDX-License-Identifier: BSD-2-Clause
4 *
5 * Copyright (c) 2011 James Gritton
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31#include <err.h>
32#include <stdlib.h>
33#include <string.h>
34
35#include "jailp.h"
36
37#ifdef DEBUG
38#define YYDEBUG 1
39#endif
40
41static struct cfjail *current_jail;
42static struct cfjail *global_jail;
43%}
44
45%union {
46	struct cfparam		*p;
47	struct cfstrings	*ss;
48	struct cfstring		*s;
49	char			*cs;
50}
51
52%token      PLEQ
53%token <cs> STR STR1 VAR VAR1
54
55%type <p>  param name
56%type <ss> value
57%type <s>  string
58
59%pure-parser
60
61%lex-param { void *scanner }
62%parse-param { void *scanner }
63
64%%
65
66/*
67 * A config file is a list of jails and parameters.  Parameters are
68 * added to the current jail, otherwise to a global pesudo-jail.
69 */
70conf	:
71	| conf jail
72	| conf param ';'
73	{
74		if (!special_param($2, scanner)) {
75			struct cfjail *j = current_jail;
76
77			if (j == NULL) {
78				if (global_jail == NULL) {
79					global_jail = add_jail();
80					global_jail->name = estrdup("*");
81				}
82				j = global_jail;
83			}
84			TAILQ_INSERT_TAIL(&j->params, $2, tq);
85		}
86	}
87	| conf ';'
88	;
89
90jail	: jail_name '{' conf '}'
91	{
92		current_jail = current_jail->cfparent;
93	}
94	;
95
96jail_name : STR
97	{
98		struct cfjail *j = add_jail();
99
100		if (current_jail == NULL)
101			j->name = $1;
102		else {
103			/*
104			 * A nested jail definition becomes
105			 * a hierarchically-named sub-jail.
106			 */
107			size_t parentlen = strlen(current_jail->name);
108			j->name = emalloc(parentlen + strlen($1) + 2);
109			strcpy(j->name, current_jail->name);
110			j->name[parentlen++] = '.';
111			strcpy(j->name + parentlen, $1);
112			free($1);
113		}
114		j->cfparent = current_jail;
115		current_jail = j;
116	}
117	;
118
119/*
120 * Parameters have a name and an optional list of value strings,
121 * which may have "+=" or "=" preceding them.
122 */
123param	: name
124	{
125		$$ = $1;
126	}
127	| name '=' value
128	{
129		$$ = $1;
130		TAILQ_CONCAT(&$$->val, $3, tq);
131		free($3);
132	}
133	| name PLEQ value
134	{
135		$$ = $1;
136		TAILQ_CONCAT(&$$->val, $3, tq);
137		$$->flags |= PF_APPEND;
138		free($3);
139	}
140	| name value
141	{
142		$$ = $1;
143		TAILQ_CONCAT(&$$->val, $2, tq);
144		$$->flags |= PF_NAMEVAL;
145		free($2);
146	}
147	| error
148	;
149
150/*
151 * A parameter has a fixed name.  A variable definition looks just like a
152 * parameter except that the name is a variable.
153 */
154name	: STR
155	{
156		$$ = emalloc(sizeof(struct cfparam));
157		$$->name = $1;
158		TAILQ_INIT(&$$->val);
159		$$->flags = 0;
160	}
161	| VAR
162	{
163		$$ = emalloc(sizeof(struct cfparam));
164		$$->name = $1;
165		TAILQ_INIT(&$$->val);
166		$$->flags = PF_VAR;
167	}
168	;
169
170value	: string
171	{
172		$$ = emalloc(sizeof(struct cfstrings));
173		TAILQ_INIT($$);
174		TAILQ_INSERT_TAIL($$, $1, tq);
175	}
176	| value ',' string
177	{
178		$$ = $1;
179		TAILQ_INSERT_TAIL($$, $3, tq);
180	}
181	;
182
183/*
184 * Strings may be passed in pieces, because of quoting and/or variable
185 * interpolation.  Reassemble them into a single string.
186 */
187string	: STR
188	{
189		$$ = emalloc(sizeof(struct cfstring));
190		$$->s = $1;
191		$$->len = strlen($1);
192		STAILQ_INIT(&$$->vars);
193	}
194	| VAR
195	{
196		struct cfvar *v;
197
198		$$ = emalloc(sizeof(struct cfstring));
199		$$->s = estrdup("");
200		$$->len = 0;
201		STAILQ_INIT(&$$->vars);
202		v = emalloc(sizeof(struct cfvar));
203		v->name = $1;
204		v->pos = 0;
205		STAILQ_INSERT_TAIL(&$$->vars, v, tq);
206	}
207	| string STR1
208	{
209		size_t len1;
210
211		$$ = $1;
212		len1 = strlen($2);
213		$$->s = erealloc($$->s, $$->len + len1 + 1);
214		strcpy($$->s + $$->len, $2);
215		free($2);
216		$$->len += len1;
217	}
218	| string VAR1
219	{
220		struct cfvar *v;
221
222		$$ = $1;
223		v = emalloc(sizeof(struct cfvar));
224		v->name = $2;
225		v->pos = $$->len;
226		STAILQ_INSERT_TAIL(&$$->vars, v, tq);
227	}
228	;
229
230%%
231
232extern int YYLEX_DECL();
233
234static void
235YYERROR_DECL()
236{
237	struct cflex *cflex = yyget_extra(scanner);
238
239	if (!yyget_text(scanner))
240		warnx("%s line %d: %s",
241		    cflex->cfname, yyget_lineno(scanner), s);
242	else if (!yyget_text(scanner)[0])
243		warnx("%s: unexpected EOF",
244		    cflex->cfname);
245	else
246		warnx("%s line %d: %s: %s",
247		    cflex->cfname, yyget_lineno(scanner),
248		    yyget_text(scanner), s);
249	cflex->error = 1;
250}
251
252/* Handle special parameters (i.e. the include directive).
253 * Return true if the parameter was specially handled.
254 */
255static int
256special_param(struct cfparam *p, void *scanner)
257{
258	if ((p->flags & (PF_VAR | PF_APPEND | PF_NAMEVAL)) != PF_NAMEVAL
259	    || strcmp(p->name, ".include"))
260		return 0;
261	struct cfstring *s;
262	TAILQ_FOREACH(s, &p->val, tq) {
263		if (STAILQ_EMPTY(&s->vars))
264			include_config(scanner, s->s);
265		else {
266			warnx("%s line %d: "
267			    "variables not permitted in '.include' filename",
268			    yyget_extra(scanner)->cfname,
269			    yyget_lineno(scanner));
270			yyget_extra(scanner)->error = 1;
271		}
272	}
273	free_param_strings(p);
274	free(p);
275	return 1;
276}
277