root/usr.sbin/jail/jailparse.y
%{
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2011 James Gritton
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>

#include "jailp.h"

#ifdef DEBUG
#define YYDEBUG 1
#endif

static struct cfjail *current_jail;
static struct cfjail *global_jail;
%}

%union {
        struct cfparam          *p;
        struct cfstrings        *ss;
        struct cfstring         *s;
        char                    *cs;
}

%token      PLEQ
%token <cs> STR STR1 VAR VAR1

%type <p>  param name
%type <ss> value
%type <s>  string

%pure-parser

%lex-param { void *scanner }
%parse-param { void *scanner }

%%

/*
 * A config file is a list of jails and parameters.  Parameters are
 * added to the current jail, otherwise to a global pesudo-jail.
 */
conf    :
        | conf jail
        | conf param ';'
        {
                if (!special_param($2, scanner)) {
                        struct cfjail *j = current_jail;

                        if (j == NULL) {
                                if (global_jail == NULL) {
                                        global_jail = add_jail();
                                        global_jail->name = estrdup("*");
                                }
                                j = global_jail;
                        }
                        TAILQ_INSERT_TAIL(&j->params, $2, tq);
                }
        }
        | conf ';'
        ;

jail    : jail_name '{' conf '}'
        {
                current_jail = current_jail->cfparent;
        }
        ;

jail_name : STR
        {
                struct cfjail *j = add_jail();

                if (current_jail == NULL)
                        j->name = $1;
                else {
                        /*
                         * A nested jail definition becomes
                         * a hierarchically-named sub-jail.
                         */
                        size_t parentlen = strlen(current_jail->name);
                        j->name = emalloc(parentlen + strlen($1) + 2);
                        strcpy(j->name, current_jail->name);
                        j->name[parentlen++] = '.';
                        strcpy(j->name + parentlen, $1);
                        free($1);
                }
                j->cfparent = current_jail;
                current_jail = j;
        }
        ;

/*
 * Parameters have a name and an optional list of value strings,
 * which may have "+=" or "=" preceding them.
 */
param   : name
        {
                $$ = $1;
        }
        | name '=' value
        {
                $$ = $1;
                TAILQ_CONCAT(&$$->val, $3, tq);
                free($3);
        }
        | name PLEQ value
        {
                $$ = $1;
                TAILQ_CONCAT(&$$->val, $3, tq);
                $$->flags |= PF_APPEND;
                free($3);
        }
        | name value
        {
                $$ = $1;
                TAILQ_CONCAT(&$$->val, $2, tq);
                $$->flags |= PF_NAMEVAL;
                free($2);
        }
        | error
        ;

/*
 * A parameter has a fixed name.  A variable definition looks just like a
 * parameter except that the name is a variable.
 */
name    : STR
        {
                $$ = emalloc(sizeof(struct cfparam));
                $$->name = $1;
                TAILQ_INIT(&$$->val);
                $$->flags = 0;
        }
        | VAR
        {
                $$ = emalloc(sizeof(struct cfparam));
                $$->name = $1;
                TAILQ_INIT(&$$->val);
                $$->flags = PF_VAR;
        }
        ;

value   : string
        {
                $$ = emalloc(sizeof(struct cfstrings));
                TAILQ_INIT($$);
                TAILQ_INSERT_TAIL($$, $1, tq);
        }
        | value ',' string
        {
                $$ = $1;
                TAILQ_INSERT_TAIL($$, $3, tq);
        }
        ;

/*
 * Strings may be passed in pieces, because of quoting and/or variable
 * interpolation.  Reassemble them into a single string.
 */
string  : STR
        {
                $$ = emalloc(sizeof(struct cfstring));
                $$->s = $1;
                $$->len = strlen($1);
                STAILQ_INIT(&$$->vars);
        }
        | VAR
        {
                struct cfvar *v;

                $$ = emalloc(sizeof(struct cfstring));
                $$->s = estrdup("");
                $$->len = 0;
                STAILQ_INIT(&$$->vars);
                v = emalloc(sizeof(struct cfvar));
                v->name = $1;
                v->pos = 0;
                STAILQ_INSERT_TAIL(&$$->vars, v, tq);
        }
        | string STR1
        {
                size_t len1;

                $$ = $1;
                len1 = strlen($2);
                $$->s = erealloc($$->s, $$->len + len1 + 1);
                strcpy($$->s + $$->len, $2);
                free($2);
                $$->len += len1;
        }
        | string VAR1
        {
                struct cfvar *v;

                $$ = $1;
                v = emalloc(sizeof(struct cfvar));
                v->name = $2;
                v->pos = $$->len;
                STAILQ_INSERT_TAIL(&$$->vars, v, tq);
        }
        ;

%%

extern int YYLEX_DECL();

static void
YYERROR_DECL()
{
        struct cflex *cflex = yyget_extra(scanner);

        if (!yyget_text(scanner))
                warnx("%s line %d: %s",
                    cflex->cfname, yyget_lineno(scanner), s);
        else if (!yyget_text(scanner)[0])
                warnx("%s: unexpected EOF",
                    cflex->cfname);
        else
                warnx("%s line %d: %s: %s",
                    cflex->cfname, yyget_lineno(scanner),
                    yyget_text(scanner), s);
        cflex->error = 1;
}

/* Handle special parameters (i.e. the include directive).
 * Return true if the parameter was specially handled.
 */
static int
special_param(struct cfparam *p, void *scanner)
{
        if ((p->flags & (PF_VAR | PF_APPEND | PF_NAMEVAL)) != PF_NAMEVAL
            || strcmp(p->name, ".include"))
                return 0;
        struct cfstring *s;
        TAILQ_FOREACH(s, &p->val, tq) {
                if (STAILQ_EMPTY(&s->vars))
                        include_config(scanner, s->s);
                else {
                        warnx("%s line %d: "
                            "variables not permitted in '.include' filename",
                            yyget_extra(scanner)->cfname,
                            yyget_lineno(scanner));
                        yyget_extra(scanner)->error = 1;
                }
        }
        free_param_strings(p);
        free(p);
        return 1;
}