diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2010-09-24 09:45:18 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2010-09-24 09:45:18 +0000 |
commit | 45558e557e0568ba9ffde3f991e669fa896646cc (patch) | |
tree | 084ae548c10be96188f20f4c4814e3c27a3ee837 /usr.sbin/iscsictl | |
parent | e05054b36677ef6434742798ae152706b6912c4d (diff) |
Import iscsictl -- the control tool for iscsid.
Currently only discovery and reload work.
OK dlg@, matthew@, deraadt@
Diffstat (limited to 'usr.sbin/iscsictl')
-rw-r--r-- | usr.sbin/iscsictl/Makefile | 18 | ||||
-rw-r--r-- | usr.sbin/iscsictl/iscsictl.c | 276 | ||||
-rw-r--r-- | usr.sbin/iscsictl/iscsictl.h | 55 | ||||
-rw-r--r-- | usr.sbin/iscsictl/parse.y | 759 | ||||
-rw-r--r-- | usr.sbin/iscsictl/parser.c | 219 |
5 files changed, 1327 insertions, 0 deletions
diff --git a/usr.sbin/iscsictl/Makefile b/usr.sbin/iscsictl/Makefile new file mode 100644 index 00000000000..bbf1468843a --- /dev/null +++ b/usr.sbin/iscsictl/Makefile @@ -0,0 +1,18 @@ +# $OpenBSD: Makefile,v 1.1 2010/09/24 09:45:17 claudio Exp $ + +.PATH: ${.CURDIR}/../iscsid + +PROG= iscsictl +SRCS= iscsictl.c parse.y parser.c +SRCS+= util.c + +MAN= + +CFLAGS+= -Wall -Werror +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare +CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../iscsid +YFLAGS= + +.include <bsd.prog.mk> diff --git a/usr.sbin/iscsictl/iscsictl.c b/usr.sbin/iscsictl/iscsictl.c new file mode 100644 index 00000000000..e9c7860d2ab --- /dev/null +++ b/usr.sbin/iscsictl/iscsictl.c @@ -0,0 +1,276 @@ +/* $OpenBSD: iscsictl.c,v 1.1 2010/09/24 09:45:17 claudio Exp $ */ + +/* + * Copyright (c) 2010 Claudio Jeker <claudio@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/un.h> + +#include <event.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "iscsid.h" +#include "iscsictl.h" + +__dead void usage(void); +void run_command(int, struct pdu *); +struct pdu *ctl_getpdu(char *, size_t); +int ctl_sendpdu(int, struct pdu *); + +char cbuf[CONTROL_READ_SIZE]; + +__dead void +usage(void) +{ + extern char *__progname; + + fprintf(stderr,"usage: %s [-s socket] command [argument ...]\n", + __progname); + exit(1); +} + +int +main (int argc, char* argv[]) +{ + struct sockaddr_un sun; + struct parse_result *res; + char *confname = ISCSID_CONFIG; + char *sockname = ISCSID_CONTROL; + struct pdu *pdu; + struct ctrlmsghdr *cmh; + struct session_config *sc; + struct session_ctlcfg *s; + struct iscsi_config *cf; + char *tname, *iname; + int ch, csock; + + /* check flags */ + while ((ch = getopt(argc, argv, "f:s:")) != -1) { + switch (ch) { + case 'f': + confname = optarg; + break; + case 's': + sockname = optarg; + break; + default: + usage(); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + + /* parse options */ + if ((res = parse(argc, argv)) == NULL) + exit(1); + + /* connect to ospfd control socket */ + if ((csock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + err(1, "socket"); + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)); + + if (connect(csock, (struct sockaddr *)&sun, sizeof(sun)) == -1) + err(1, "connect: %s", sockname); + + switch (res->action) { + case NONE: + case LOG_VERBOSE: + case LOG_BRIEF: + case SHOW: + case SHOW_SUM: + usage(); + /* NOTREACHED */ + case RELOAD: + if ((cf = parse_config(confname)) == NULL) + errx(1, "errors while loading configuration file."); + SIMPLEQ_FOREACH(s, &cf->sessions, entry) { + if ((pdu = pdu_new()) == NULL) + err(1, "pdu_new"); + if ((cmh = pdu_alloc(sizeof(*cmh))) == NULL) + err(1, "pdu_alloc"); + bzero(cmh, sizeof(*cmh)); + cmh->type = CTRL_SESSION_CONFIG; + cmh->len[0] = sizeof(*sc); + if ((sc = pdu_dup(&s->session, sizeof(s->session))) == + NULL) + err(1, "pdu_dup"); + if (s->session.TargetName) { + if ((tname = pdu_dup(s->session.TargetName, + strlen(s->session.TargetName) + 1)) == + NULL) + err(1, "pdu_dup"); + cmh->len[1] = strlen(s->session.TargetName) + 1; + } else + tname = NULL; + if (s->session.InitiatorName) { + if ((iname = pdu_dup(s->session.InitiatorName, + strlen(s->session.InitiatorName) + 1)) == + NULL) + err(1, "pdu_dup"); + cmh->len[2] = strlen(s->session.InitiatorName) + + 1; + } else + iname = NULL; + pdu_addbuf(pdu, cmh, sizeof(*cmh), 0); + pdu_addbuf(pdu, sc, sizeof(*sc), 1); + if (tname) + pdu_addbuf(pdu, tname, strlen(tname) + 1, 2); + if (iname) + pdu_addbuf(pdu, iname, strlen(iname) + 1, 3); + + run_command(csock, pdu); + } + break; + case DISCOVERY: + printf("discover %s\n", log_sockaddr(&res->addr)); + if ((pdu = pdu_new()) == NULL) + err(1, "pdu_new"); + if ((cmh = pdu_alloc(sizeof(*cmh))) == NULL) + err(1, "pdu_alloc"); + if ((sc = pdu_alloc(sizeof(*sc))) == NULL) + err(1, "pdu_alloc"); + bzero(cmh, sizeof(*cmh)); + bzero(sc, sizeof(*sc)); + snprintf(sc->SessionName, sizeof(sc->SessionName), + "discovery.%d", (int)getpid()); + bcopy(&res->addr, &sc->connection.TargetAddr, res->addr.ss_len); + sc->SessionType = SESSION_TYPE_DISCOVERY; + cmh->type = CTRL_SESSION_CONFIG; + cmh->len[0] = sizeof(*sc); + pdu_addbuf(pdu, cmh, sizeof(*cmh), 0); + pdu_addbuf(pdu, sc, sizeof(*sc), 1); + + run_command(csock, pdu); + } +printf("sent pdu to daemon\n"); + + close(csock); + + return (0); +} + +void +run_command(int csock, struct pdu *pdu) +{ + struct ctrlmsghdr *cmh; + int done = 0; + ssize_t n; + + if (ctl_sendpdu(csock, pdu) == -1) + err(1, "send"); + while (!done) { + if ((n = recv(csock, cbuf, sizeof(cbuf), 0)) == -1 && + !(errno == EAGAIN || errno == EINTR)) + err(1, "recv"); + + if (n == 0) + errx(1, "connection to iscsid closed"); + + pdu = ctl_getpdu(cbuf, n); + cmh = pdu_getbuf(pdu, NULL, 0); + if (cmh == NULL) + break; + switch (cmh->type) { + case CTRL_SUCCESS: + printf("command successful\n"); + done = 1; + break; + case CTRL_FAILURE: + printf("command failed\n"); + done = 1; + break; + } + } +} + +struct pdu * +ctl_getpdu(char *buf, size_t len) +{ + struct pdu *p; + struct ctrlmsghdr *cmh; + void *data; + size_t n; + int i; + + if (len < sizeof(*cmh)) + return NULL; + + if (!(p = pdu_new())) + return NULL; + + n = sizeof(*cmh); + cmh = pdu_alloc(n); + bcopy(buf, cmh, n); + buf += n; + len -= n; + + if (pdu_addbuf(p, cmh, n, 0)) { + free(cmh); +fail: + pdu_free(p); + return NULL; + } + + for (i = 0; i < 3; i++) { + n = cmh->len[i]; + if (n == 0) + continue; + if (PDU_LEN(n) > len) + goto fail; + if (!(data = pdu_alloc(n))) + goto fail; + bcopy(buf, data, n); + if (pdu_addbuf(p, data, n, i + 1)) { + free(data); + goto fail; + } + buf += PDU_LEN(n); + len -= PDU_LEN(n); + } + + return p; +} + +int +ctl_sendpdu(int fd, struct pdu *pdu) +{ + struct iovec iov[PDU_MAXIOV]; + struct msghdr msg; + unsigned int niov = 0; + + for (niov = 0; niov < PDU_MAXIOV; niov++) { + iov[niov].iov_base = pdu->iov[niov].iov_base; + iov[niov].iov_len = pdu->iov[niov].iov_len; + } + bzero(&msg, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = niov; + if (sendmsg(fd, &msg, 0) == -1) + return -1; + return 0; +} diff --git a/usr.sbin/iscsictl/iscsictl.h b/usr.sbin/iscsictl/iscsictl.h new file mode 100644 index 00000000000..0768cfacc5e --- /dev/null +++ b/usr.sbin/iscsictl/iscsictl.h @@ -0,0 +1,55 @@ +/* $OpenBSD: iscsictl.h,v 1.1 2010/09/24 09:45:17 claudio Exp $ */ + +/* + * Copyright (c) 2009 David Gwynne <dlg@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define ISCSID_OPT_NOACTION 0x01 + +struct iscsi_config { + SIMPLEQ_HEAD(, session_ctlcfg) sessions; +}; + +struct session_ctlcfg { + struct session_config session; + SIMPLEQ_ENTRY(session_ctlcfg) entry; +}; + +enum actions { + NONE, + LOG_VERBOSE, + LOG_BRIEF, + SHOW, + SHOW_SUM, + RELOAD, + DISCOVERY +}; + +struct parse_result { + struct sockaddr_storage addr; + int flags; + enum actions action; + u_int8_t prefixlen; +}; + +/* parse.y */ +struct iscsi_config * parse_config(char *); +int cmdline_symset(char *); + +/* parser.c */ +struct parse_result *parse(int, char *[]); +const struct token *match_token(const char *, const struct token *); +void show_valid_args(const struct token *); +int parse_addr(const char *, struct sockaddr_storage *); diff --git a/usr.sbin/iscsictl/parse.y b/usr.sbin/iscsictl/parse.y new file mode 100644 index 00000000000..337cbe7d7d3 --- /dev/null +++ b/usr.sbin/iscsictl/parse.y @@ -0,0 +1,759 @@ +/* $OpenBSD: parse.y,v 1.1 2010/09/24 09:45:17 claudio Exp $ */ + +/* + * Copyright (c) 2010 David Gwynne <dlg@openbsd.org> + * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> + * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> + * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. + * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +%{ +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <event.h> +#include <limits.h> +#include <netdb.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "iscsid.h" +#include "iscsictl.h" + +TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); +static struct file { + TAILQ_ENTRY(file) entry; + FILE *stream; + char *name; + int lineno; + int errors; +} *file, *topfile; +struct file *pushfile(const char *, int); +int popfile(void); +int yyparse(void); +int yylex(void); +int yyerror(const char *, ...); +int kw_cmp(const void *, const void *); +int lookup(char *); +int lgetc(int); +int lungetc(int); +int findeol(void); + +void clear_config(struct iscsi_config *); + +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); +struct sym { + TAILQ_ENTRY(sym) entry; + int used; + int persist; + char *nam; + char *val; +}; +int symset(const char *, const char *, int); +char *symget(const char *); + +static int errors; +static struct iscsi_config *conf; +static struct session_config *session; + +struct addrinfo_opts { + int af; + char *port; +} addrinfo_opts; + +typedef struct { + union { + int i; + int64_t number; + char *string; + struct addrinfo_opts addrinfo_opts; + struct addrinfo *addrinfo; + } v; + int lineno; +} YYSTYPE; + +%} + +%token TARGET TARGETNAME TARGETADDR +%token INITIATORNAME INITIATORADDR +%token ENABLED DISABLED NORMAL DISCOVERY +%token ADDRESS INET INET6 PORT +%token INCLUDE +%token ERROR +%token <v.string> STRING +%token <v.number> NUMBER +%type <v.i> af state type +%type <v.string> port +%type <v.addrinfo> addrinfo +%type <v.addrinfo_opts> addrinfo_opts addrinfo_opts_l addrinfo_opt +%type <v.string> string + +%% + +grammar : /* empty */ + | grammar '\n' + | grammar include '\n' + | grammar varset '\n' + | grammar target '\n' + | grammar error '\n' { file->errors++; } + ; + +include : INCLUDE STRING { + struct file *nfile; + + if ((nfile = pushfile($2, 1)) == NULL) { + yyerror("failed to include file %s", $2); + free($2); + YYERROR; + } + free($2); + + file = nfile; + lungetc('\n'); + } + ; + +string : string STRING { + if (asprintf(&$$, "%s %s", $1, $2) == -1) { + free($1); + free($2); + yyerror("string: asprintf"); + YYERROR; + } + free($1); + free($2); + } + | STRING + ; + +varset : STRING '=' string { + if (symset($1, $3, 0) == -1) + err(1, "cannot store variable"); + free($1); + free($3); + } + ; + +optnl : '\n' optnl + | + ; + +nl : '\n' optnl /* one or more newlines */ + ; + +target : TARGET STRING { + struct session_ctlcfg *scelm; + + scelm = calloc(1, sizeof(*scelm)); + session = &scelm->session; + if (strlcpy(session->SessionName, $2, + sizeof(session->SessionName)) >= + sizeof(session->SessionName)) { + free($2); + free(scelm); + yyerror("target name \"%s\" too long"); + YYERROR; + } + free($2); + SIMPLEQ_INSERT_TAIL(&conf->sessions, scelm, entry); + } '{' optnl targetopts_l '}' + ; + +targetopts_l : targetopts_l targetoptsl nl + | targetoptsl optnl + ; + +targetoptsl : state { session->disabled = $1; } + | type { session->SessionType = $1; } + | TARGETNAME STRING { session->TargetName = $2; } + | INITIATORNAME STRING { session->InitiatorName = $2; } + | TARGETADDR addrinfo { + bcopy($2->ai_addr, &session->connection.TargetAddr, + $2->ai_addr->sa_len); + freeaddrinfo($2); + } + | INITIATORADDR addrinfo { + bcopy($2->ai_addr, &session->connection.LocalAddr, + $2->ai_addr->sa_len); + freeaddrinfo($2); + } + ; + +addrinfo : STRING addrinfo_opts { + struct addrinfo hints; + char *hostname; + int error; + + $$ = NULL; + + if ($2.port == NULL) { + if (($2.port = strdup("iscsi")) == NULL) { + free($1); + yyerror("port strdup"); + YYERROR; + } + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = $2.af; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (strcmp($1, "*") == 0) { + hostname = NULL; + hints.ai_flags = AI_PASSIVE; + } else + hostname = $1; + + error = getaddrinfo(hostname, $2.port, &hints, &$$); + if (error) { + yyerror("%s (%s %s)", gai_strerror(error), + $1, $2.port); + free($1); + free($2.port); + YYERROR; + } + + free($1); + free($2.port); + } + ; + +addrinfo_opts : { + addrinfo_opts.port = NULL; + addrinfo_opts.af = PF_UNSPEC; + } + addrinfo_opts_l { $$ = addrinfo_opts; } + | /* empty */ { + addrinfo_opts.port = NULL; + addrinfo_opts.af = PF_UNSPEC; + $$ = addrinfo_opts; + } + ; + +addrinfo_opts_l : addrinfo_opts_l addrinfo_opt + | addrinfo_opt + ; + +addrinfo_opt : port { + if (addrinfo_opts.port != NULL) { + yyerror("port cannot be redefined"); + YYERROR; + } + addrinfo_opts.port = $1; + } + | af { + if (addrinfo_opts.af != PF_UNSPEC) { + yyerror("address family cannot be redefined"); + YYERROR; + } + addrinfo_opts.af = $1; + } + ; + +port : PORT STRING { $$ = $2; } + ; + +af : INET { $$ = PF_INET; } + | INET6 { $$ = PF_INET6; } + ; + +state : ENABLED { $$ = 0; } + | DISABLED { $$ = 1; } + ; + +type : NORMAL { $$ = SESSION_TYPE_NORMAL; } + | DISCOVERY { $$ = SESSION_TYPE_DISCOVERY; } + ; + + +%% + +struct keywords { + const char *k_name; + int k_val; +}; + +int +yyerror(const char *fmt, ...) +{ + va_list ap; + + file->errors++; + va_start(ap, fmt); + fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + return (0); +} + +int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + {"address", ADDRESS}, + {"disabled", DISABLED}, + {"discovery", DISCOVERY}, + {"enabled", ENABLED}, + {"include", INCLUDE}, + {"inet", INET}, + {"inet4", INET}, + {"inet6", INET6}, + {"initiatoraddr", INITIATORADDR}, + {"initiatorname", INITIATORNAME}, + {"normal", NORMAL}, + {"port", PORT}, + {"target", TARGET}, + {"targetaddr", TARGETADDR}, + {"targetname", TARGETNAME} + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) + return (p->k_val); + else + return (STRING); +} + +#define MAXPUSHBACK 128 + +char *parsebuf; +int parseindex; +char pushback_buffer[MAXPUSHBACK]; +int pushback_index; + +int +lgetc(int quotec) +{ + int c, next; + + if (parsebuf) { + /* Read character from the parsebuffer instead of input. */ + if (parseindex >= 0) { + c = parsebuf[parseindex++]; + if (c != '\0') + return (c); + parsebuf = NULL; + } else + parseindex++; + } + + if (pushback_index) + return (pushback_buffer[--pushback_index]); + + if (quotec) { + if ((c = getc(file->stream)) == EOF) { + yyerror("reached end of file while parsing " + "quoted string"); + if (file == topfile || popfile() == EOF) + return (EOF); + return (quotec); + } + return (c); + } + + while ((c = getc(file->stream)) == '\\') { + next = getc(file->stream); + if (next != '\n') { + c = next; + break; + } + yylval.lineno = file->lineno; + file->lineno++; + } + + while (c == EOF) { + if (file == topfile || popfile() == EOF) + return (EOF); + c = getc(file->stream); + } + return (c); +} + +int +lungetc(int c) +{ + if (c == EOF) + return (EOF); + if (parsebuf) { + parseindex--; + if (parseindex >= 0) + return (c); + } + if (pushback_index < MAXPUSHBACK-1) + return (pushback_buffer[pushback_index++] = c); + else + return (EOF); +} + +int +findeol(void) +{ + int c; + + parsebuf = NULL; + + /* skip to either EOF or the first real EOL */ + while (1) { + if (pushback_index) + c = pushback_buffer[--pushback_index]; + else + c = lgetc(0); + if (c == '\n') { + file->lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + char buf[8096]; + char *p, *val; + int quotec, next, c; + int token; + +top: + p = buf; + while ((c = lgetc(0)) == ' ' || c == '\t') + ; /* nothing */ + + yylval.lineno = file->lineno; + if (c == '#') + while ((c = lgetc(0)) != '\n' && c != EOF) + ; /* nothing */ + if (c == '$' && parsebuf == NULL) { + while (1) { + if ((c = lgetc(0)) == EOF) + return (0); + + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + if (isalnum(c) || c == '_') { + *p++ = (char)c; + continue; + } + *p = '\0'; + lungetc(c); + break; + } + val = symget(buf); + if (val == NULL) { + yyerror("macro '%s' not defined", buf); + return (findeol()); + } + parsebuf = val; + parseindex = 0; + goto top; + } + + switch (c) { + case '\'': + case '"': + quotec = c; + while (1) { + if ((c = lgetc(quotec)) == EOF) + return (0); + if (c == '\n') { + file->lineno++; + continue; + } else if (c == '\\') { + if ((next = lgetc(quotec)) == EOF) + return (0); + if (next == quotec || c == ' ' || c == '\t') + c = next; + else if (next == '\n') { + file->lineno++; + continue; + } else + lungetc(next); + } else if (c == quotec) { + *p = '\0'; + break; + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = (char)c; + } + yylval.v.string = strdup(buf); + if (yylval.v.string == NULL) + err(1, "yylex: strdup"); + return (STRING); + } + +#define allowed_to_end_number(x) \ + (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') + + if (c == '-' || isdigit(c)) { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && isdigit(c)); + lungetc(c); + if (p == buf + 1 && buf[0] == '-') + goto nodigits; + if (c == EOF || allowed_to_end_number(c)) { + const char *errstr = NULL; + + *p = '\0'; + yylval.v.number = strtonum(buf, LLONG_MIN, + LLONG_MAX, &errstr); + if (errstr) { + yyerror("\"%s\" invalid number: %s", + buf, errstr); + return (findeol()); + } + return (NUMBER); + } else { +nodigits: + while (p > buf + 1) + lungetc(*--p); + c = *--p; + if (c == '-') + return (c); + } + } + +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && \ + x != '!' && x != '=' && x != '#' && \ + x != ',')) + + if (isalnum(c) || c == ':' || c == '_') { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + if ((yylval.v.string = strdup(buf)) == NULL) + err(1, "yylex: strdup"); + return (token); + } + if (c == '\n') { + yylval.lineno = file->lineno; + file->lineno++; + } + if (c == EOF) + return (0); + return (c); +} + +struct file * +pushfile(const char *name, int secret) +{ + struct file *nfile; + + if ((nfile = calloc(1, sizeof(struct file))) == NULL) { + warn("malloc"); + return (NULL); + } + if ((nfile->name = strdup(name)) == NULL) { + warn("malloc"); + free(nfile); + return (NULL); + } + if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { + warn("%s", nfile->name); + free(nfile->name); + free(nfile); + return (NULL); + } + nfile->lineno = 1; + TAILQ_INSERT_TAIL(&files, nfile, entry); + return (nfile); +} + +int +popfile(void) +{ + struct file *prev; + + if ((prev = TAILQ_PREV(file, files, entry)) != NULL) + prev->errors += file->errors; + + TAILQ_REMOVE(&files, file, entry); + fclose(file->stream); + free(file->name); + free(file); + file = prev; + return (file ? 0 : EOF); +} + +struct iscsi_config * +parse_config(char *filename) +{ + struct sym *sym, *next; + + file = pushfile(filename, 1); + if (file == NULL) + return (NULL); + topfile = file; + + conf = calloc(1, sizeof(struct iscsi_config)); + if (conf == NULL) + return (NULL); + SIMPLEQ_INIT(&conf->sessions); + + yyparse(); + errors = file->errors; + popfile(); + + /* Free macros and check which have not been used. */ + for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { + next = TAILQ_NEXT(sym, entry); + if (!sym->persist) { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } + + if (errors) { + clear_config(conf); + return (NULL); + } + + return (conf); +} + +int +cmdline_symset(char *s) +{ + char *sym, *val; + int ret; + size_t len; + + if ((val = strrchr(s, '=')) == NULL) + return (-1); + + len = strlen(s) - strlen(val) + 1; + if ((sym = malloc(len)) == NULL) + errx(1, "cmdline_symset: malloc"); + + strlcpy(sym, s, len); + + ret = symset(sym, val + 1, 1); + free(sym); + + return (ret); +} + +char * +symget(const char *nam) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entry) + if (strcmp(nam, sym->nam) == 0) { + sym->used = 1; + return (sym->val); + } + return (NULL); +} + +int +symset(const char *nam, const char *val, int persist) +{ + struct sym *sym; + + for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); + sym = TAILQ_NEXT(sym, entry)) + ; /* nothing */ + + if (sym != NULL) { + if (sym->persist == 1) + return (0); + else { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } + if ((sym = calloc(1, sizeof(*sym))) == NULL) + return (-1); + + sym->nam = strdup(nam); + if (sym->nam == NULL) { + free(sym); + return (-1); + } + sym->val = strdup(val); + if (sym->val == NULL) { + free(sym->nam); + free(sym); + return (-1); + } + sym->used = 0; + sym->persist = persist; + TAILQ_INSERT_TAIL(&symhead, sym, entry); + return (0); +} + +void +clear_config(struct iscsi_config *c) +{ + struct session_ctlcfg *s; + + while ((s = SIMPLEQ_FIRST(&c->sessions))) { + SIMPLEQ_REMOVE_HEAD(&c->sessions, entry); + free(s->session.TargetName); + free(s->session.InitiatorName); + free(s); + } + + free(c); +} diff --git a/usr.sbin/iscsictl/parser.c b/usr.sbin/iscsictl/parser.c new file mode 100644 index 00000000000..39ad9659ac9 --- /dev/null +++ b/usr.sbin/iscsictl/parser.c @@ -0,0 +1,219 @@ +/* $OpenBSD: parser.c,v 1.1 2010/09/24 09:45:17 claudio Exp $ */ + +/* + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <err.h> +#include <errno.h> +#include <event.h> +#include <limits.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "iscsid.h" +#include "iscsictl.h" + +enum token_type { + NOTOKEN, + ENDTOKEN, + KEYWORD, + ADDRESS, + FLAG +}; + +struct token { + enum token_type type; + const char *keyword; + int value; + const struct token *next; +}; + +static const struct token t_main[]; +static const struct token t_show[]; +static const struct token t_log[]; +static const struct token t_discovery[]; + +static const struct token t_main[] = { + {KEYWORD, "reload", RELOAD, NULL}, + {KEYWORD, "discover", DISCOVERY, t_discovery}, + {KEYWORD, "show", SHOW, t_show}, + {KEYWORD, "log", NONE, t_log}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show[] = { + {NOTOKEN, "", NONE, NULL}, + {KEYWORD, "summary", SHOW_SUM, NULL}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_log[] = { + {KEYWORD, "verbose", LOG_VERBOSE, NULL}, + {KEYWORD, "brief", LOG_BRIEF, NULL}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_discovery[] = { + {ADDRESS, "", NONE, NULL}, + {ENDTOKEN, "", NONE, NULL} +}; + +static struct parse_result res; + +struct parse_result * +parse(int argc, char *argv[]) +{ + const struct token *table = t_main; + const struct token *match; + + bzero(&res, sizeof(res)); + + while (argc >= 0) { + if ((match = match_token(argv[0], table)) == NULL) { + fprintf(stderr, "valid commands/args:\n"); + show_valid_args(table); + return (NULL); + } + + argc--; + argv++; + + if (match->type == NOTOKEN || match->next == NULL) + break; + + table = match->next; + } + + if (argc > 0) { + fprintf(stderr, "superfluous argument: %s\n", argv[0]); + return (NULL); + } + + return (&res); +} + +const struct token * +match_token(const char *word, const struct token *table) +{ + u_int i, match; + const struct token *t = NULL; + + match = 0; + + for (i = 0; table[i].type != ENDTOKEN; i++) { + switch (table[i].type) { + case NOTOKEN: + if (word == NULL || strlen(word) == 0) { + match++; + t = &table[i]; + } + break; + case KEYWORD: + if (word != NULL && strncmp(word, table[i].keyword, + strlen(word)) == 0) { + match++; + t = &table[i]; + if (t->value) + res.action = t->value; + } + break; + case FLAG: + if (word != NULL && strncmp(word, table[i].keyword, + strlen(word)) == 0) { + match++; + t = &table[i]; + res.flags |= t->value; + } + break; + case ADDRESS: + if (!parse_addr(word, &res.addr)) { + match++; + t = &table[i]; + if (t->value) + res.action = t->value; + } + break; + case ENDTOKEN: + break; + } + } + + if (match != 1) { + if (word == NULL) + fprintf(stderr, "missing argument:\n"); + else if (match > 1) + fprintf(stderr, "ambiguous argument: %s\n", word); + else if (match < 1) + fprintf(stderr, "unknown argument: %s\n", word); + return (NULL); + } + + return (t); +} + +void +show_valid_args(const struct token *table) +{ + int i; + + for (i = 0; table[i].type != ENDTOKEN; i++) { + switch (table[i].type) { + case NOTOKEN: + fprintf(stderr, " <cr>\n"); + break; + case KEYWORD: + case FLAG: + fprintf(stderr, " %s\n", table[i].keyword); + break; + case ADDRESS: + fprintf(stderr, " <address>\n"); + break; + case ENDTOKEN: + break; + } + } +} + +int +parse_addr(const char *word, struct sockaddr_storage *sa) +{ + struct addrinfo hints, *addrs; + int rv; + + bzero(&hints, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if ((rv = getaddrinfo(word, "iscsi", &hints, &addrs)) == 0) { + if (sizeof(*sa) < addrs->ai_addrlen) + err(1, "parse_host: bork bork bork"); + bcopy(addrs->ai_addr, sa, addrs->ai_addrlen); + freeaddrinfo(addrs); + return (0); + } + + errx(1, "parse_host: %s", gai_strerror(rv)); +} |