freebsd-dev/usr.sbin/ctld/parse.y
Marcelo Araujo 8951f05525 Rework CTL frontend & backend options to use nv(3), allow creating multiple
ioctl frontend ports.

This revision introduces two changes to CTL:
- Changes the way options are passed to CTL_LUN_REQ and CTL_PORT_REQ ioctls.
  Removes ctl_be_arg structure and associated logic and replaces it with
  nv(3)-based logic for passing in and out arguments.
- Allows creating multiple ioctl frontend ports using either ctladm(8) or
  ctld(8).
  New frontend ports are represented by /dev/cam/ctl<pp>.<vp> nodes, eg /dev/cam/ctl5.3.
  Those device nodes respond only to CTL_IO ioctl.

New command-line options for ctladm:
# creates new ioctl frontend port with using free pp and vp=0
ctladm port -c
# creates new ioctl frontend port with pp=10 and vp=0
ctladm port -c -O pp=10
# creates new ioctl frontend port with pp=11 and vp=12
ctladm port -c -O pp=11 -O vp=12
# removes port with number 4 (it's a "targ_port" number, not pp number)
ctladm port -r -p 4

New syntax for ctl.conf:
target ... {
    port ioctl/<pp>
    ...
}

target ... {
    port ioctl/<pp>/<vp>
    ...

Note: Most of this work was made by jceel@, thank you.

Submitted by:	jceel
Reworked by:	myself
Reviewed by:	mav (earlier versions and recently during the rework)
Obtained from:  FreeNAS and TrueOS
Relnotes:	Yes
Sponsored by:	iXsystems Inc.
Differential Revision:	https://reviews.freebsd.org/D9299
2018-05-10 03:50:20 +00:00

1095 lines
19 KiB
Plaintext

%{
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
* 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.
*
* $FreeBSD$
*/
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ctld.h"
extern FILE *yyin;
extern char *yytext;
extern int lineno;
static struct conf *conf = NULL;
static struct auth_group *auth_group = NULL;
static struct portal_group *portal_group = NULL;
static struct target *target = NULL;
static struct lun *lun = NULL;
extern void yyerror(const char *);
extern int yylex(void);
extern void yyrestart(FILE *);
%}
%token ALIAS AUTH_GROUP AUTH_TYPE BACKEND BLOCKSIZE CHAP CHAP_MUTUAL
%token CLOSING_BRACKET CTL_LUN DEBUG DEVICE_ID DEVICE_TYPE
%token DISCOVERY_AUTH_GROUP DISCOVERY_FILTER FOREIGN
%token INITIATOR_NAME INITIATOR_PORTAL ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT
%token LISTEN LISTEN_ISER LUN MAXPROC OFFLOAD OPENING_BRACKET OPTION
%token PATH PIDFILE PORT PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR
%token TAG TARGET TIMEOUT
%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
|
lun
|
target
;
debug: DEBUG STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
conf->conf_debug = tmp;
}
;
timeout: TIMEOUT STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
conf->conf_timeout = tmp;
}
;
maxproc: MAXPROC STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
conf->conf_maxproc = tmp;
}
;
pidfile: PIDFILE STR
{
if (conf->conf_pidfile_path != NULL) {
log_warnx("pidfile specified more than once");
free($2);
return (1);
}
conf->conf_pidfile_path = $2;
}
;
isns_server: ISNS_SERVER STR
{
int error;
error = isns_new(conf, $2);
free($2);
if (error != 0)
return (1);
}
;
isns_period: ISNS_PERIOD STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
conf->conf_isns_period = tmp;
}
;
isns_timeout: ISNS_TIMEOUT STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
conf->conf_isns_timeout = tmp;
}
;
auth_group: AUTH_GROUP auth_group_name
OPENING_BRACKET auth_group_entries CLOSING_BRACKET
{
auth_group = NULL;
}
;
auth_group_name: STR
{
/*
* Make it possible to redefine default
* auth-group. but only once.
*/
if (strcmp($1, "default") == 0 &&
conf->conf_default_ag_defined == false) {
auth_group = auth_group_find(conf, $1);
conf->conf_default_ag_defined = true;
} else {
auth_group = auth_group_new(conf, $1);
}
free($1);
if (auth_group == NULL)
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_initiator_name
|
auth_group_initiator_portal
;
auth_group_auth_type: AUTH_TYPE STR
{
int error;
error = auth_group_set_type(auth_group, $2);
free($2);
if (error != 0)
return (1);
}
;
auth_group_chap: CHAP STR STR
{
const struct auth *ca;
ca = auth_new_chap(auth_group, $2, $3);
free($2);
free($3);
if (ca == NULL)
return (1);
}
;
auth_group_chap_mutual: CHAP_MUTUAL STR STR STR STR
{
const struct auth *ca;
ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5);
free($2);
free($3);
free($4);
free($5);
if (ca == NULL)
return (1);
}
;
auth_group_initiator_name: INITIATOR_NAME STR
{
const struct auth_name *an;
an = auth_name_new(auth_group, $2);
free($2);
if (an == NULL)
return (1);
}
;
auth_group_initiator_portal: INITIATOR_PORTAL STR
{
const struct auth_portal *ap;
ap = auth_portal_new(auth_group, $2);
free($2);
if (ap == NULL)
return (1);
}
;
portal_group: PORTAL_GROUP portal_group_name
OPENING_BRACKET portal_group_entries CLOSING_BRACKET
{
portal_group = NULL;
}
;
portal_group_name: STR
{
/*
* Make it possible to redefine default
* portal-group. but only once.
*/
if (strcmp($1, "default") == 0 &&
conf->conf_default_pg_defined == false) {
portal_group = portal_group_find(conf, $1);
conf->conf_default_pg_defined = true;
} else {
portal_group = portal_group_new(conf, $1);
}
free($1);
if (portal_group == NULL)
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_discovery_auth_group: DISCOVERY_AUTH_GROUP STR
{
if (portal_group->pg_discovery_auth_group != NULL) {
log_warnx("discovery-auth-group for portal-group "
"\"%s\" specified more than once",
portal_group->pg_name);
return (1);
}
portal_group->pg_discovery_auth_group =
auth_group_find(conf, $2);
if (portal_group->pg_discovery_auth_group == NULL) {
log_warnx("unknown discovery-auth-group \"%s\" "
"for portal-group \"%s\"",
$2, portal_group->pg_name);
return (1);
}
free($2);
}
;
portal_group_discovery_filter: DISCOVERY_FILTER STR
{
int error;
error = portal_group_set_filter(portal_group, $2);
free($2);
if (error != 0)
return (1);
}
;
portal_group_foreign: FOREIGN
{
portal_group->pg_foreign = 1;
}
;
portal_group_listen: LISTEN STR
{
int error;
error = portal_group_add_listen(portal_group, $2, false);
free($2);
if (error != 0)
return (1);
}
;
portal_group_listen_iser: LISTEN_ISER STR
{
int error;
error = portal_group_add_listen(portal_group, $2, true);
free($2);
if (error != 0)
return (1);
}
;
portal_group_offload: OFFLOAD STR
{
int error;
error = portal_group_set_offload(portal_group, $2);
free($2);
if (error != 0)
return (1);
}
;
portal_group_option: OPTION STR STR
{
struct option *o;
o = option_new(&portal_group->pg_options, $2, $3);
free($2);
free($3);
if (o == NULL)
return (1);
}
;
portal_group_redirect: REDIRECT STR
{
int error;
error = portal_group_set_redirection(portal_group, $2);
free($2);
if (error != 0)
return (1);
}
;
portal_group_tag: TAG STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
portal_group->pg_tag = tmp;
}
;
lun: LUN lun_name
OPENING_BRACKET lun_entries CLOSING_BRACKET
{
lun = NULL;
}
;
lun_name: STR
{
lun = lun_new(conf, $1);
free($1);
if (lun == NULL)
return (1);
}
;
target: TARGET target_name
OPENING_BRACKET target_entries CLOSING_BRACKET
{
target = NULL;
}
;
target_name: STR
{
target = target_new(conf, $1);
free($1);
if (target == NULL)
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
{
if (target->t_alias != NULL) {
log_warnx("alias for target \"%s\" "
"specified more than once", target->t_name);
return (1);
}
target->t_alias = $2;
}
;
target_auth_group: AUTH_GROUP STR
{
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL)
log_warnx("auth-group for target \"%s\" "
"specified more than once", target->t_name);
else
log_warnx("cannot use both auth-group and explicit "
"authorisations for target \"%s\"",
target->t_name);
return (1);
}
target->t_auth_group = auth_group_find(conf, $2);
if (target->t_auth_group == NULL) {
log_warnx("unknown auth-group \"%s\" for target "
"\"%s\"", $2, target->t_name);
return (1);
}
free($2);
}
;
target_auth_type: AUTH_TYPE STR
{
int error;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot use both auth-group and "
"auth-type for target \"%s\"",
target->t_name);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
return (1);
}
target->t_auth_group->ag_target = target;
}
error = auth_group_set_type(target->t_auth_group, $2);
free($2);
if (error != 0)
return (1);
}
;
target_chap: CHAP STR STR
{
const struct auth *ca;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot use both auth-group and "
"chap for target \"%s\"",
target->t_name);
free($2);
free($3);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
free($3);
return (1);
}
target->t_auth_group->ag_target = target;
}
ca = auth_new_chap(target->t_auth_group, $2, $3);
free($2);
free($3);
if (ca == NULL)
return (1);
}
;
target_chap_mutual: CHAP_MUTUAL STR STR STR STR
{
const struct auth *ca;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot use both auth-group and "
"chap-mutual for target \"%s\"",
target->t_name);
free($2);
free($3);
free($4);
free($5);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
free($3);
free($4);
free($5);
return (1);
}
target->t_auth_group->ag_target = target;
}
ca = auth_new_chap_mutual(target->t_auth_group,
$2, $3, $4, $5);
free($2);
free($3);
free($4);
free($5);
if (ca == NULL)
return (1);
}
;
target_initiator_name: INITIATOR_NAME STR
{
const struct auth_name *an;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot use both auth-group and "
"initiator-name for target \"%s\"",
target->t_name);
free($2);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
return (1);
}
target->t_auth_group->ag_target = target;
}
an = auth_name_new(target->t_auth_group, $2);
free($2);
if (an == NULL)
return (1);
}
;
target_initiator_portal: INITIATOR_PORTAL STR
{
const struct auth_portal *ap;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot use both auth-group and "
"initiator-portal for target \"%s\"",
target->t_name);
free($2);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
return (1);
}
target->t_auth_group->ag_target = target;
}
ap = auth_portal_new(target->t_auth_group, $2);
free($2);
if (ap == NULL)
return (1);
}
;
target_portal_group: PORTAL_GROUP STR STR
{
struct portal_group *tpg;
struct auth_group *tag;
struct port *tp;
tpg = portal_group_find(conf, $2);
if (tpg == NULL) {
log_warnx("unknown portal-group \"%s\" for target "
"\"%s\"", $2, target->t_name);
free($2);
free($3);
return (1);
}
tag = auth_group_find(conf, $3);
if (tag == NULL) {
log_warnx("unknown auth-group \"%s\" for target "
"\"%s\"", $3, target->t_name);
free($2);
free($3);
return (1);
}
tp = port_new(conf, target, tpg);
if (tp == NULL) {
log_warnx("can't link portal-group \"%s\" to target "
"\"%s\"", $2, target->t_name);
free($2);
return (1);
}
tp->p_auth_group = tag;
free($2);
free($3);
}
| PORTAL_GROUP STR
{
struct portal_group *tpg;
struct port *tp;
tpg = portal_group_find(conf, $2);
if (tpg == NULL) {
log_warnx("unknown portal-group \"%s\" for target "
"\"%s\"", $2, target->t_name);
free($2);
return (1);
}
tp = port_new(conf, target, tpg);
if (tp == NULL) {
log_warnx("can't link portal-group \"%s\" to target "
"\"%s\"", $2, target->t_name);
free($2);
return (1);
}
free($2);
}
;
target_port: PORT STR
{
struct pport *pp;
struct port *tp;
int ret, i_pp, i_vp = 0;
ret = sscanf($2, "ioctl/%d/%d", &i_pp, &i_vp);
if (ret > 0) {
tp = port_new_ioctl(conf, target, i_pp, i_vp);
if (tp == NULL) {
log_warnx("can't create new ioctl port for "
"target \"%s\"", target->t_name);
free($2);
return (1);
}
} else {
pp = pport_find(conf, $2);
if (pp == NULL) {
log_warnx("unknown port \"%s\" for target \"%s\"",
$2, target->t_name);
free($2);
return (1);
}
if (!TAILQ_EMPTY(&pp->pp_ports)) {
log_warnx("can't link port \"%s\" to target \"%s\", "
"port already linked to some target",
$2, target->t_name);
free($2);
return (1);
}
tp = port_new_pp(conf, target, pp);
if (tp == NULL) {
log_warnx("can't link port \"%s\" to target \"%s\"",
$2, target->t_name);
free($2);
return (1);
}
}
free($2);
}
;
target_redirect: REDIRECT STR
{
int error;
error = target_set_redirection(target, $2);
free($2);
if (error != 0)
return (1);
}
;
target_lun: LUN lun_number
OPENING_BRACKET lun_entries CLOSING_BRACKET
{
lun = NULL;
}
;
lun_number: STR
{
uint64_t tmp;
int ret;
char *name;
if (expand_number($1, &tmp) != 0) {
yyerror("invalid numeric value");
free($1);
return (1);
}
if (tmp >= MAX_LUNS) {
yyerror("LU number is too big");
free($1);
return (1);
}
ret = asprintf(&name, "%s,lun,%ju", target->t_name, tmp);
if (ret <= 0)
log_err(1, "asprintf");
lun = lun_new(conf, name);
if (lun == NULL)
return (1);
lun_set_scsiname(lun, name);
target->t_luns[tmp] = lun;
}
;
target_lun_ref: LUN STR STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
free($3);
return (1);
}
free($2);
if (tmp >= MAX_LUNS) {
yyerror("LU number is too big");
free($3);
return (1);
}
lun = lun_find(conf, $3);
free($3);
if (lun == NULL)
return (1);
target->t_luns[tmp] = lun;
}
;
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
{
if (lun->l_backend != NULL) {
log_warnx("backend for lun \"%s\" "
"specified more than once",
lun->l_name);
free($2);
return (1);
}
lun_set_backend(lun, $2);
free($2);
}
;
lun_blocksize: BLOCKSIZE STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
if (lun->l_blocksize != 0) {
log_warnx("blocksize for lun \"%s\" "
"specified more than once",
lun->l_name);
return (1);
}
lun_set_blocksize(lun, tmp);
}
;
lun_device_id: DEVICE_ID STR
{
if (lun->l_device_id != NULL) {
log_warnx("device_id for lun \"%s\" "
"specified more than once",
lun->l_name);
free($2);
return (1);
}
lun_set_device_id(lun, $2);
free($2);
}
;
lun_device_type: DEVICE_TYPE STR
{
uint64_t tmp;
if (strcasecmp($2, "disk") == 0 ||
strcasecmp($2, "direct") == 0)
tmp = 0;
else if (strcasecmp($2, "processor") == 0)
tmp = 3;
else if (strcasecmp($2, "cd") == 0 ||
strcasecmp($2, "cdrom") == 0 ||
strcasecmp($2, "dvd") == 0 ||
strcasecmp($2, "dvdrom") == 0)
tmp = 5;
else if (expand_number($2, &tmp) != 0 ||
tmp > 15) {
yyerror("invalid numeric value");
free($2);
return (1);
}
lun_set_device_type(lun, tmp);
}
;
lun_ctl_lun: CTL_LUN STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
if (lun->l_ctl_lun >= 0) {
log_warnx("ctl_lun for lun \"%s\" "
"specified more than once",
lun->l_name);
return (1);
}
lun_set_ctl_lun(lun, tmp);
}
;
lun_option: OPTION STR STR
{
struct option *o;
o = option_new(&lun->l_options, $2, $3);
free($2);
free($3);
if (o == NULL)
return (1);
}
;
lun_path: PATH STR
{
if (lun->l_path != NULL) {
log_warnx("path for lun \"%s\" "
"specified more than once",
lun->l_name);
free($2);
return (1);
}
lun_set_path(lun, $2);
free($2);
}
;
lun_serial: SERIAL STR
{
if (lun->l_serial != NULL) {
log_warnx("serial for lun \"%s\" "
"specified more than once",
lun->l_name);
free($2);
return (1);
}
lun_set_serial(lun, $2);
free($2);
}
;
lun_size: SIZE STR
{
uint64_t tmp;
if (expand_number($2, &tmp) != 0) {
yyerror("invalid numeric value");
free($2);
return (1);
}
if (lun->l_size != 0) {
log_warnx("size for lun \"%s\" "
"specified more than once",
lun->l_name);
return (1);
}
lun_set_size(lun, tmp);
}
;
%%
void
yyerror(const char *str)
{
log_warnx("error in configuration file at line %d near '%s': %s",
lineno, yytext, str);
}
int
parse_conf(struct conf *newconf, const char *path)
{
int error;
conf = newconf;
yyin = fopen(path, "r");
if (yyin == NULL) {
log_warn("unable to open configuration file %s", path);
return (1);
}
lineno = 1;
yyrestart(yyin);
error = yyparse();
auth_group = NULL;
portal_group = NULL;
target = NULL;
lun = NULL;
fclose(yyin);
return (error);
}