root/usr.sbin/ctld/parse.y
%{
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2012 The FreeBSD Foundation
 *
 * This software was developed by Edward Tomasz Napierala under sponsorship
 * from the FreeBSD Foundation.
 *
 * 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/types.h>
#include <sys/nv.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <assert.h>
#include <libiscsiutil.h>
#include <libutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/ip.h>

#include "conf.h"

extern FILE *yyin;
extern char *yytext;
extern int lineno;

extern void     yyerror(const char *);
extern void     yyrestart(FILE *);

%}

%token ALIAS AUTH_GROUP AUTH_TYPE BACKEND BLOCKSIZE CHAP CHAP_MUTUAL
%token CLOSING_BRACKET CONTROLLER CTL_LUN DEBUG DEVICE_ID DEVICE_TYPE
%token DISCOVERY_AUTH_GROUP DISCOVERY_FILTER DISCOVERY_TCP DSCP FOREIGN
%token HOST_ADDRESS HOST_NQN
%token INITIATOR_NAME INITIATOR_PORTAL ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT
%token LISTEN LISTEN_ISER LUN MAXPROC NAMESPACE
%token OFFLOAD OPENING_BRACKET OPTION
%token PATH PCP PIDFILE PORT PORTAL_GROUP REDIRECT SEMICOLON SERIAL
%token SIZE STR TAG TARGET TCP TIMEOUT TRANSPORT_GROUP
%token AF11 AF12 AF13 AF21 AF22 AF23 AF31 AF32 AF33 AF41 AF42 AF43
%token BE EF CS0 CS1 CS2 CS3 CS4 CS5 CS6 CS7

%union
{
        char *str;
}

%token <str> STR

%%

statements:
        |
        statements statement
        |
        statements statement SEMICOLON
        ;

statement:
        debug
        |
        timeout
        |
        maxproc
        |
        pidfile
        |
        isns_server
        |
        isns_period
        |
        isns_timeout
        |
        auth_group
        |
        portal_group
        |
        transport_group
        |
        lun
        |
        target
        |
        controller
        ;

debug:          DEBUG STR
        {
                int64_t tmp;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        return (1);
                }
                free($2);

                conf_set_debug(tmp);
        }
        ;

timeout:        TIMEOUT STR
        {
                int64_t tmp;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        return (1);
                }
                free($2);

                conf_set_timeout(tmp);
        }
        ;

maxproc:        MAXPROC STR
        {
                int64_t tmp;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        return (1);
                }
                free($2);

                conf_set_maxproc(tmp);
        }
        ;

pidfile:        PIDFILE STR
        {
                bool ok;

                ok = conf_set_pidfile_path($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

isns_server:    ISNS_SERVER STR
        {
                bool ok;

                ok = isns_add_server($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

isns_period:    ISNS_PERIOD STR
        {
                int64_t tmp;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        return (1);
                }
                free($2);

                conf_set_isns_period(tmp);
        }
        ;

isns_timeout:   ISNS_TIMEOUT STR
        {
                int64_t tmp;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        return (1);
                }
                free($2);

                conf_set_isns_timeout(tmp);
        }
        ;

auth_group:     AUTH_GROUP auth_group_name
    OPENING_BRACKET auth_group_entries CLOSING_BRACKET
        {
                auth_group_finish();
        }
        ;

auth_group_name:        STR
        {
                bool ok;

                ok = auth_group_start($1);
                free($1);
                if (!ok)
                        return (1);
        }
        ;

auth_group_entries:
        |
        auth_group_entries auth_group_entry
        |
        auth_group_entries auth_group_entry SEMICOLON
        ;

auth_group_entry:
        auth_group_auth_type
        |
        auth_group_chap
        |
        auth_group_chap_mutual
        |
        auth_group_host_address
        |
        auth_group_host_nqn
        |
        auth_group_initiator_name
        |
        auth_group_initiator_portal
        ;

auth_group_auth_type:   AUTH_TYPE STR
        {
                bool ok;

                ok = auth_group_set_type($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

auth_group_chap:        CHAP STR STR
        {
                bool ok;

                ok = auth_group_add_chap($2, $3);
                free($2);
                free($3);
                if (!ok)
                        return (1);
        }
        ;

auth_group_chap_mutual: CHAP_MUTUAL STR STR STR STR
        {
                bool ok;

                ok = auth_group_add_chap_mutual($2, $3, $4, $5);
                free($2);
                free($3);
                free($4);
                free($5);
                if (!ok)
                        return (1);
        }
        ;

auth_group_host_address:        HOST_ADDRESS STR
        {
                bool ok;

                ok = auth_group_add_host_address($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

auth_group_host_nqn:    HOST_NQN STR
        {
                bool ok;

                ok = auth_group_add_host_nqn($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

auth_group_initiator_name:      INITIATOR_NAME STR
        {
                bool ok;

                ok = auth_group_add_initiator_name($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

auth_group_initiator_portal:    INITIATOR_PORTAL STR
        {
                bool ok;

                ok = auth_group_add_initiator_portal($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

portal_group:   PORTAL_GROUP portal_group_name
    OPENING_BRACKET portal_group_entries CLOSING_BRACKET
        {
                portal_group_finish();
        }
        ;

portal_group_name:      STR
        {
                bool ok;

                ok = portal_group_start($1);
                free($1);
                if (!ok)
                        return (1);
        }
        ;

portal_group_entries:
        |
        portal_group_entries portal_group_entry
        |
        portal_group_entries portal_group_entry SEMICOLON
        ;

portal_group_entry:
        portal_group_discovery_auth_group
        |
        portal_group_discovery_filter
        |
        portal_group_foreign
        |
        portal_group_listen
        |
        portal_group_listen_iser
        |
        portal_group_offload
        |
        portal_group_option
        |
        portal_group_redirect
        |
        portal_group_tag
        |
        portal_group_dscp
        |
        portal_group_pcp
        ;

portal_group_discovery_auth_group:      DISCOVERY_AUTH_GROUP STR
        {
                bool ok;

                ok = portal_group_set_discovery_auth_group($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

portal_group_discovery_filter:  DISCOVERY_FILTER STR
        {
                bool ok;

                ok = portal_group_set_filter($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

portal_group_foreign:   FOREIGN
        {

                portal_group_set_foreign();
        }
        ;

portal_group_listen:    LISTEN STR
        {
                bool ok;

                ok = portal_group_add_listen($2, false);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

portal_group_listen_iser:       LISTEN_ISER STR
        {
                bool ok;

                ok = portal_group_add_listen($2, true);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

portal_group_offload:   OFFLOAD STR
        {
                bool ok;

                ok = portal_group_set_offload($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

portal_group_option:    OPTION STR STR
        {
                bool ok;

                ok = portal_group_add_option($2, $3);
                free($2);
                free($3);
                if (!ok)
                        return (1);
        }
        ;

portal_group_redirect:  REDIRECT STR
        {
                bool ok;

                ok = portal_group_set_redirection($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

portal_group_tag:       TAG STR
        {
                int64_t tmp;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        return (1);
                }
                free($2);

                portal_group_set_tag(tmp);
        }
        ;

portal_group_dscp
: DSCP STR
        {
                int64_t tmp;

                if (strcmp($2, "0x") == 0) {
                        tmp = strtol($2 + 2, NULL, 16);
                } else if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        return (1);
                }
                free($2);

                if (!portal_group_set_dscp(tmp))
                        return (1);
        }
| DSCP BE       { portal_group_set_dscp(IPTOS_DSCP_CS0  >> 2); }
| DSCP EF       { portal_group_set_dscp(IPTOS_DSCP_EF   >> 2); }
| DSCP CS0      { portal_group_set_dscp(IPTOS_DSCP_CS0  >> 2); }
| DSCP CS1      { portal_group_set_dscp(IPTOS_DSCP_CS1  >> 2); }
| DSCP CS2      { portal_group_set_dscp(IPTOS_DSCP_CS2  >> 2); }
| DSCP CS3      { portal_group_set_dscp(IPTOS_DSCP_CS3  >> 2); }
| DSCP CS4      { portal_group_set_dscp(IPTOS_DSCP_CS4  >> 2); }
| DSCP CS5      { portal_group_set_dscp(IPTOS_DSCP_CS5  >> 2); }
| DSCP CS6      { portal_group_set_dscp(IPTOS_DSCP_CS6  >> 2); }
| DSCP CS7      { portal_group_set_dscp(IPTOS_DSCP_CS7  >> 2); }
| DSCP AF11     { portal_group_set_dscp(IPTOS_DSCP_AF11 >> 2); }
| DSCP AF12     { portal_group_set_dscp(IPTOS_DSCP_AF12 >> 2); }
| DSCP AF13     { portal_group_set_dscp(IPTOS_DSCP_AF13 >> 2); }
| DSCP AF21     { portal_group_set_dscp(IPTOS_DSCP_AF21 >> 2); }
| DSCP AF22     { portal_group_set_dscp(IPTOS_DSCP_AF22 >> 2); }
| DSCP AF23     { portal_group_set_dscp(IPTOS_DSCP_AF23 >> 2); }
| DSCP AF31     { portal_group_set_dscp(IPTOS_DSCP_AF31 >> 2); }
| DSCP AF32     { portal_group_set_dscp(IPTOS_DSCP_AF32 >> 2); }
| DSCP AF33     { portal_group_set_dscp(IPTOS_DSCP_AF33 >> 2); }
| DSCP AF41     { portal_group_set_dscp(IPTOS_DSCP_AF41 >> 2); }
| DSCP AF42     { portal_group_set_dscp(IPTOS_DSCP_AF42 >> 2); }
| DSCP AF43     { portal_group_set_dscp(IPTOS_DSCP_AF43 >> 2); }
        ;

portal_group_pcp:       PCP STR
        {
                int64_t tmp;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        return (1);
                }
                free($2);

                if (!portal_group_set_pcp(tmp))
                        return (1);
        }
        ;

transport_group:        TRANSPORT_GROUP transport_group_name
    OPENING_BRACKET transport_group_entries CLOSING_BRACKET
        {
                portal_group_finish();
        }
        ;

transport_group_name:   STR
        {
                bool ok;

                ok = transport_group_start($1);
                free($1);
                if (!ok)
                        return (1);
        }
        ;

transport_group_entries:
        |
        transport_group_entries transport_group_entry
        |
        transport_group_entries transport_group_entry SEMICOLON
        ;

transport_group_entry:
        portal_group_discovery_auth_group
        |
        portal_group_discovery_filter
        |
        transport_group_listen_discovery_tcp
        |
        transport_group_listen_tcp
        |
        portal_group_option
        |
        portal_group_tag
        |
        portal_group_dscp
        |
        portal_group_pcp
        ;

transport_group_listen_discovery_tcp:   LISTEN DISCOVERY_TCP STR
        {
                bool ok;

                ok = transport_group_add_listen_discovery_tcp($3);
                free($3);
                if (!ok)
                        return (1);
        }
        ;

transport_group_listen_tcp:     LISTEN TCP STR
        {
                bool ok;

                ok = transport_group_add_listen_tcp($3);
                free($3);
                if (!ok)
                        return (1);
        }
        ;

lun:    LUN lun_name
    OPENING_BRACKET lun_entries CLOSING_BRACKET
        {
                lun_finish();
        }
        ;

lun_name:       STR
        {
                bool ok;

                ok = lun_start($1);
                free($1);
                if (!ok)
                        return (1);
        }
        ;

target: TARGET target_name
    OPENING_BRACKET target_entries CLOSING_BRACKET
        {
                target_finish();
        }
        ;

target_name:    STR
        {
                bool ok;

                ok = target_start($1);
                free($1);
                if (!ok)
                        return (1);
        }
        ;

target_entries:
        |
        target_entries target_entry
        |
        target_entries target_entry SEMICOLON
        ;

target_entry:
        target_alias
        |
        target_auth_group
        |
        target_auth_type
        |
        target_chap
        |
        target_chap_mutual
        |
        target_initiator_name
        |
        target_initiator_portal
        |
        target_portal_group
        |
        target_port
        |
        target_redirect
        |
        target_lun
        |
        target_lun_ref
        ;

target_alias:   ALIAS STR
        {
                bool ok;

                ok = target_set_alias($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

target_auth_group:      AUTH_GROUP STR
        {
                bool ok;

                ok = target_set_auth_group($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

target_auth_type:       AUTH_TYPE STR
        {
                bool ok;

                ok = target_set_auth_type($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

target_chap:    CHAP STR STR
        {
                bool ok;

                ok = target_add_chap($2, $3);
                free($2);
                free($3);
                if (!ok)
                        return (1);
        }
        ;

target_chap_mutual:     CHAP_MUTUAL STR STR STR STR
        {
                bool ok;

                ok = target_add_chap_mutual($2, $3, $4, $5);
                free($2);
                free($3);
                free($4);
                free($5);
                if (!ok)
                        return (1);
        }
        ;

target_initiator_name:  INITIATOR_NAME STR
        {
                bool ok;

                ok = target_add_initiator_name($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

target_initiator_portal:        INITIATOR_PORTAL STR
        {
                bool ok;

                ok = target_add_initiator_portal($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

target_portal_group:    PORTAL_GROUP STR STR
        {
                bool ok;

                ok = target_add_portal_group($2, $3);
                free($2);
                free($3);
                if (!ok)
                        return (1);
        }
        |               PORTAL_GROUP STR
        {
                bool ok;

                ok = target_add_portal_group($2, NULL);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

target_port:    PORT STR
        {
                bool ok;

                ok = target_set_physical_port($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

target_redirect:        REDIRECT STR
        {
                bool ok;

                ok = target_set_redirection($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

target_lun:     LUN lun_number
    OPENING_BRACKET lun_entries CLOSING_BRACKET
        {
                lun_finish();
        }
        ;

lun_number:     STR
        {
                int64_t tmp;

                if (expand_number($1, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($1);
                        return (1);
                }
                free($1);

                if (!target_start_lun(tmp))
                        return (1);
        }
        ;

target_lun_ref: LUN STR STR
        {
                int64_t tmp;
                bool ok;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        free($3);
                        return (1);
                }
                free($2);

                ok = target_add_lun(tmp, $3);
                free($3);
                if (!ok)
                        return (1);
        }
        ;

controller:     CONTROLLER controller_name
    OPENING_BRACKET controller_entries CLOSING_BRACKET
        {
                target_finish();
        }
        ;

controller_name:        STR
        {
                bool ok;

                ok = controller_start($1);
                free($1);
                if (!ok)
                        return (1);
        }
        ;

controller_entries:
        |
        controller_entries controller_entry
        |
        controller_entries controller_entry SEMICOLON
        ;

controller_entry:
        target_auth_group
        |
        target_auth_type
        |
        controller_host_address
        |
        controller_host_nqn
        |
        controller_transport_group
        |
        controller_namespace
        |
        controller_namespace_ref
        ;

controller_host_address:        HOST_ADDRESS STR
        {
                bool ok;

                ok = controller_add_host_address($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

controller_host_nqn:    HOST_NQN STR
        {
                bool ok;

                ok = controller_add_host_nqn($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

controller_transport_group:     TRANSPORT_GROUP STR STR
        {
                bool ok;

                ok = target_add_portal_group($2, $3);
                free($2);
                free($3);
                if (!ok)
                        return (1);
        }
        |               TRANSPORT_GROUP STR
        {
                bool ok;

                ok = target_add_portal_group($2, NULL);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

controller_namespace:   NAMESPACE ns_number
    OPENING_BRACKET lun_entries CLOSING_BRACKET
        {
                lun_finish();
        }
        ;

ns_number:      STR
        {
                uint64_t tmp;

                if (expand_number($1, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($1);
                        return (1);
                }
                free($1);

                if (!controller_start_namespace(tmp))
                        return (1);
        }
        ;

controller_namespace_ref:       NAMESPACE STR STR
        {
                uint64_t tmp;
                bool ok;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        free($3);
                        return (1);
                }
                free($2);

                ok = controller_add_namespace(tmp, $3);
                free($3);
                if (!ok)
                        return (1);
        }
        ;

lun_entries:
        |
        lun_entries lun_entry
        |
        lun_entries lun_entry SEMICOLON
        ;

lun_entry:
        lun_backend
        |
        lun_blocksize
        |
        lun_device_id
        |
        lun_device_type
        |
        lun_ctl_lun
        |
        lun_option
        |
        lun_path
        |
        lun_serial
        |
        lun_size
        ;

lun_backend:    BACKEND STR
        {
                bool ok;

                ok = lun_set_backend($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

lun_blocksize:  BLOCKSIZE STR
        {
                int64_t tmp;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        return (1);
                }
                free($2);

                if (!lun_set_blocksize(tmp))
                        return (1);
        }
        ;

lun_device_id:  DEVICE_ID STR
        {
                bool ok;

                ok = lun_set_device_id($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

lun_device_type:        DEVICE_TYPE STR
        {
                bool ok;

                ok = lun_set_device_type($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

lun_ctl_lun:    CTL_LUN STR
        {
                int64_t tmp;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        return (1);
                }
                free($2);

                if (!lun_set_ctl_lun(tmp))
                        return (1);
        }
        ;

lun_option:     OPTION STR STR
        {
                bool ok;

                ok = lun_add_option($2, $3);
                free($2);
                free($3);
                if (!ok)
                        return (1);
        }
        ;

lun_path:       PATH STR
        {
                bool ok;

                ok = lun_set_path($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

lun_serial:     SERIAL STR
        {
                bool ok;

                ok = lun_set_serial($2);
                free($2);
                if (!ok)
                        return (1);
        }
        ;

lun_size:       SIZE STR
        {
                int64_t tmp;

                if (expand_number($2, &tmp) != 0) {
                        yyerror("invalid numeric value");
                        free($2);
                        return (1);
                }
                free($2);

                if (!lun_set_size(tmp))
                        return (1);
        }
        ;
%%

void
yyerror(const char *str)
{

        log_warnx("error in configuration file at line %d near '%s': %s",
            lineno, yytext, str);
}

bool
yyparse_conf(FILE *fp)
{
        int error;

        yyin = fp;
        lineno = 1;
        yyrestart(yyin);
        error = yyparse();

        return (error == 0);
}