xref: /openbsd-src/usr.sbin/iscsictl/parse.y (revision 08f6ba1906d01c4a24fa700d568400f71bcf9611)
1 /*	$OpenBSD: parse.y,v 1.19 2021/10/15 15:01:28 naddy Exp $ */
2 
3 /*
4  * Copyright (c) 2010 David Gwynne <dlg@openbsd.org>
5  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
7  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
8  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
9  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
10  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
11  *
12  * Permission to use, copy, modify, and distribute this software for any
13  * purpose with or without fee is hereby granted, provided that the above
14  * copyright notice and this permission notice appear in all copies.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  */
24 
25 %{
26 #include <sys/types.h>
27 #include <sys/queue.h>
28 #include <sys/socket.h>
29 #include <sys/stat.h>
30 #include <sys/uio.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <ctype.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <event.h>
37 #include <limits.h>
38 #include <netdb.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 #include <scsi/iscsi.h>
45 #include "iscsid.h"
46 #include "iscsictl.h"
47 
48 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
49 static struct file {
50 	TAILQ_ENTRY(file)	 entry;
51 	FILE			*stream;
52 	char			*name;
53 	size_t			 ungetpos;
54 	size_t			 ungetsize;
55 	u_char			*ungetbuf;
56 	int			 eof_reached;
57 	int			 lineno;
58 	int			 errors;
59 } *file, *topfile;
60 struct file	*pushfile(const char *, int);
61 int		 popfile(void);
62 int		 yyparse(void);
63 int		 yylex(void);
64 int		 yyerror(const char *, ...)
65     __attribute__((__format__ (printf, 1, 2)))
66     __attribute__((__nonnull__ (1)));
67 int		 kw_cmp(const void *, const void *);
68 int		 lookup(char *);
69 int		 igetc(void);
70 int		 lgetc(int);
71 void		 lungetc(int);
72 int		 findeol(void);
73 
74 void		 clear_config(struct iscsi_config *);
75 
76 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
77 struct sym {
78 	TAILQ_ENTRY(sym)	 entry;
79 	int			 used;
80 	int			 persist;
81 	char			*nam;
82 	char			*val;
83 };
84 int		 symset(const char *, const char *, int);
85 char		*symget(const char *);
86 
87 static int			 errors;
88 static struct iscsi_config	*conf;
89 static struct session_config	*session;
90 
91 struct addrinfo_opts {
92 	int	af;
93 	char	*port;
94 } addrinfo_opts;
95 
96 typedef struct {
97 	union {
98 		int			 i;
99 		int64_t			 number;
100 		char			*string;
101 		struct addrinfo_opts	 addrinfo_opts;
102 		struct addrinfo		*addrinfo;
103 	} v;
104 	int lineno;
105 } YYSTYPE;
106 
107 %}
108 
109 %token  TARGET TARGETNAME TARGETADDR
110 %token	INITIATORNAME INITIATORADDR ISID
111 %token	ENABLED DISABLED NORMAL DISCOVERY
112 %token  ADDRESS INET INET6 PORT
113 %token	INCLUDE
114 %token	ERROR
115 %token	<v.string>		STRING
116 %token	<v.number>		NUMBER
117 %type	<v.i>			af state type
118 %type	<v.string>		port
119 %type	<v.addrinfo>		addrinfo
120 %type	<v.addrinfo_opts>	addrinfo_opts addrinfo_opts_l addrinfo_opt
121 %type	<v.string>		string
122 
123 %%
124 
125 grammar		: /* empty */
126 		| grammar '\n'
127 		| grammar include '\n'
128 		| grammar varset '\n'
129 		| grammar initiator '\n'
130 		| grammar target '\n'
131 		| grammar error '\n'		{ file->errors++; }
132 		;
133 
134 include		: INCLUDE STRING		{
135 			struct file	*nfile;
136 
137 			if ((nfile = pushfile($2, 1)) == NULL) {
138 				yyerror("failed to include file %s", $2);
139 				free($2);
140 				YYERROR;
141 			}
142 			free($2);
143 
144 			file = nfile;
145 			lungetc('\n');
146 		}
147 		;
148 
149 string		: string STRING	{
150 			if (asprintf(&$$, "%s %s", $1, $2) == -1) {
151 				free($1);
152 				free($2);
153 				yyerror("string: asprintf");
154 				YYERROR;
155 			}
156 			free($1);
157 			free($2);
158 		}
159 		| STRING
160 		;
161 
162 varset		: STRING '=' string		{
163 			char *s = $1;
164 			while (*s++) {
165 				if (isspace((unsigned char)*s)) {
166 					yyerror("macro name cannot contain "
167 					    "whitespace");
168 					free($1);
169 					free($3);
170 					YYERROR;
171 				}
172 			}
173 			if (symset($1, $3, 0) == -1)
174 				err(1, "cannot store variable");
175 			free($1);
176 			free($3);
177 		}
178 		;
179 
180 optnl		: '\n' optnl
181 		|
182 		;
183 
184 nl		: '\n' optnl		/* one or more newlines */
185 		;
186 
187 initiator	: ISID STRING NUMBER NUMBER {
188 			u_int32_t mask1, mask2;
189 
190 			if (!strcasecmp($2, "oui")) {
191 				conf->initiator.isid_base = ISCSI_ISID_OUI;
192 				mask1 = 0x3fffff00;
193 				mask2 = 0x000000ff;
194 			} else if (!strcasecmp($2, "en")) {
195 				conf->initiator.isid_base = ISCSI_ISID_EN;
196 				mask1 = 0x00ffffff;
197 				mask2 = 0;
198 			} else if (!strcasecmp($2, "rand")) {
199 				conf->initiator.isid_base = ISCSI_ISID_RAND;
200 				mask1 = 0x00ffffff;
201 				mask2 = 0;
202 			} else {
203 				yyerror("isid type %s unknown", $2);
204 				free($2);
205 				YYERROR;
206 			}
207 			free($2);
208 			conf->initiator.isid_base |= $3 & mask1;
209 			conf->initiator.isid_base |= ($4 >> 16) & mask2;
210 			conf->initiator.isid_qual = $4;
211 		}
212 		;
213 
214 target		: TARGET STRING {
215 			struct session_ctlcfg *scelm;
216 
217 			scelm = calloc(1, sizeof(*scelm));
218 			session = &scelm->session;
219 			if (strlcpy(session->SessionName, $2,
220 			    sizeof(session->SessionName)) >=
221 			    sizeof(session->SessionName)) {
222 				yyerror("target name \"%s\" too long", $2);
223 				free($2);
224 				free(scelm);
225 				YYERROR;
226 			}
227 			free($2);
228 			SIMPLEQ_INSERT_TAIL(&conf->sessions, scelm, entry);
229 		} '{' optnl targetopts_l '}'
230 		;
231 
232 targetopts_l	: targetopts_l targetoptsl nl
233 		| targetoptsl optnl
234 		;
235 
236 targetoptsl	: state			{ session->disabled = $1; }
237 		| type			{ session->SessionType = $1; }
238 		| TARGETNAME STRING	{ session->TargetName = $2; }
239 		| INITIATORNAME STRING	{ session->InitiatorName = $2; }
240 		| TARGETADDR addrinfo	{
241 			bcopy($2->ai_addr, &session->connection.TargetAddr,
242 			    $2->ai_addr->sa_len);
243 			freeaddrinfo($2);
244 		}
245 		| INITIATORADDR addrinfo {
246 			((struct sockaddr_in *)$2->ai_addr)->sin_port = 0;
247 			bcopy($2->ai_addr, &session->connection.LocalAddr,
248 			    $2->ai_addr->sa_len);
249 			freeaddrinfo($2);
250 		}
251 		;
252 
253 addrinfo	: STRING addrinfo_opts {
254 			struct addrinfo hints;
255 			char *hostname;
256 			int error;
257 
258 			$$ = NULL;
259 
260 			if ($2.port == NULL) {
261 				if (($2.port = strdup("iscsi")) == NULL) {
262 					free($1);
263 					yyerror("port strdup");
264 					YYERROR;
265 				}
266 			}
267 
268 			memset(&hints, 0, sizeof(hints));
269 			hints.ai_family = $2.af;
270 			hints.ai_socktype = SOCK_STREAM;
271 			hints.ai_protocol = IPPROTO_TCP;
272 
273 			if (strcmp($1, "*") == 0) {
274 				hostname = NULL;
275 				hints.ai_flags = AI_PASSIVE;
276 			} else
277 				hostname = $1;
278 
279 			error = getaddrinfo(hostname, $2.port, &hints, &$$);
280 			if (error) {
281 				yyerror("%s (%s %s)", gai_strerror(error),
282 				    $1, $2.port);
283 				free($1);
284 				free($2.port);
285 				YYERROR;
286 			}
287 
288 			free($1);
289 			free($2.port);
290 		}
291 		;
292 
293 addrinfo_opts	: {
294 			addrinfo_opts.port = NULL;
295 			addrinfo_opts.af = PF_UNSPEC;
296 		}
297 			addrinfo_opts_l { $$ = addrinfo_opts; }
298 		| /* empty */ {
299 			addrinfo_opts.port = NULL;
300 			addrinfo_opts.af = PF_UNSPEC;
301 			$$ = addrinfo_opts;
302 		}
303 		;
304 
305 addrinfo_opts_l	: addrinfo_opts_l addrinfo_opt
306 		| addrinfo_opt
307 		;
308 
309 addrinfo_opt	: port {
310 			if (addrinfo_opts.port != NULL) {
311 				yyerror("port cannot be redefined");
312 				YYERROR;
313 			}
314 			addrinfo_opts.port = $1;
315 		}
316 		| af {
317 			if (addrinfo_opts.af != PF_UNSPEC) {
318 				yyerror("address family cannot be redefined");
319 				YYERROR;
320 			}
321 			addrinfo_opts.af = $1;
322 		}
323 		;
324 
325 port		: PORT STRING			{ $$ = $2; }
326 		;
327 
328 af		: INET				{ $$ = PF_INET; }
329 		| INET6				{ $$ = PF_INET6; }
330 		;
331 
332 state		: ENABLED			{ $$ = 0; }
333 		| DISABLED			{ $$ = 1; }
334 		;
335 
336 type		: NORMAL			{ $$ = SESSION_TYPE_NORMAL; }
337 		| DISCOVERY			{ $$ = SESSION_TYPE_DISCOVERY; }
338 		;
339 
340 
341 %%
342 
343 struct keywords {
344 	const char	*k_name;
345 	int		 k_val;
346 };
347 
348 int
yyerror(const char * fmt,...)349 yyerror(const char *fmt, ...)
350 {
351 	va_list	ap;
352 
353 	file->errors++;
354 	va_start(ap, fmt);
355 	fprintf(stderr, "%s:%d: ", file->name, yylval.lineno);
356 	vfprintf(stderr, fmt, ap);
357 	fprintf(stderr, "\n");
358 	va_end(ap);
359 	return (0);
360 }
361 
362 int
kw_cmp(const void * k,const void * e)363 kw_cmp(const void *k, const void *e)
364 {
365 	return (strcmp(k, ((const struct keywords *)e)->k_name));
366 }
367 
368 int
lookup(char * s)369 lookup(char *s)
370 {
371 	/* this has to be sorted always */
372 	static const struct keywords keywords[] = {
373 		{"address",		ADDRESS},
374 		{"disabled",		DISABLED},
375 		{"discovery",		DISCOVERY},
376 		{"enabled",		ENABLED},
377 		{"include",		INCLUDE},
378 		{"inet",		INET},
379 		{"inet4",		INET},
380 		{"inet6",		INET6},
381 		{"initiatoraddr",	INITIATORADDR},
382 		{"initiatorname",	INITIATORNAME},
383 		{"isid",		ISID},
384 		{"normal",		NORMAL},
385 		{"port",		PORT},
386 		{"target",		TARGET},
387 		{"targetaddr",		TARGETADDR},
388 		{"targetname",		TARGETNAME}
389 	};
390 	const struct keywords	*p;
391 
392 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
393 	    sizeof(keywords[0]), kw_cmp);
394 
395 	if (p)
396 		return (p->k_val);
397 	else
398 		return (STRING);
399 }
400 
401 #define START_EXPAND	1
402 #define DONE_EXPAND	2
403 
404 static int	expanding;
405 
406 int
igetc(void)407 igetc(void)
408 {
409 	int	c;
410 
411 	while (1) {
412 		if (file->ungetpos > 0)
413 			c = file->ungetbuf[--file->ungetpos];
414 		else
415 			c = getc(file->stream);
416 
417 		if (c == START_EXPAND)
418 			expanding = 1;
419 		else if (c == DONE_EXPAND)
420 			expanding = 0;
421 		else
422 			break;
423 	}
424 	return (c);
425 }
426 
427 int
lgetc(int quotec)428 lgetc(int quotec)
429 {
430 	int		c, next;
431 
432 	if (quotec) {
433 		if ((c = igetc()) == EOF) {
434 			yyerror("reached end of file while parsing "
435 			    "quoted string");
436 			if (file == topfile || popfile() == EOF)
437 				return (EOF);
438 			return (quotec);
439 		}
440 		return (c);
441 	}
442 
443 	while ((c = igetc()) == '\\') {
444 		next = igetc();
445 		if (next != '\n') {
446 			c = next;
447 			break;
448 		}
449 		yylval.lineno = file->lineno;
450 		file->lineno++;
451 	}
452 
453 	while (c == EOF) {
454 		if (file == topfile || popfile() == EOF)
455 			return (EOF);
456 		c = getc(file->stream);
457 	}
458 	return (c);
459 }
460 
461 void
lungetc(int c)462 lungetc(int c)
463 {
464 	if (c == EOF)
465 		return;
466 
467 	if (file->ungetpos >= file->ungetsize) {
468 		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
469 		if (p == NULL)
470 			err(1, "%s", __func__);
471 		file->ungetbuf = p;
472 		file->ungetsize *= 2;
473 	}
474 	file->ungetbuf[file->ungetpos++] = c;
475 }
476 
477 int
findeol(void)478 findeol(void)
479 {
480 	int	c;
481 
482 	/* skip to either EOF or the first real EOL */
483 	while (1) {
484 		c = lgetc(0);
485 		if (c == '\n') {
486 			file->lineno++;
487 			break;
488 		}
489 		if (c == EOF)
490 			break;
491 	}
492 	return (ERROR);
493 }
494 
495 int
yylex(void)496 yylex(void)
497 {
498 	char	 buf[8096];
499 	char	*p, *val;
500 	int	 quotec, next, c;
501 	int	 token;
502 
503 top:
504 	p = buf;
505 	while ((c = lgetc(0)) == ' ' || c == '\t')
506 		; /* nothing */
507 
508 	yylval.lineno = file->lineno;
509 	if (c == '#')
510 		while ((c = lgetc(0)) != '\n' && c != EOF)
511 			; /* nothing */
512 	if (c == '$' && !expanding) {
513 		while (1) {
514 			if ((c = lgetc(0)) == EOF)
515 				return (0);
516 
517 			if (p + 1 >= buf + sizeof(buf) - 1) {
518 				yyerror("string too long");
519 				return (findeol());
520 			}
521 			if (isalnum(c) || c == '_') {
522 				*p++ = c;
523 				continue;
524 			}
525 			*p = '\0';
526 			lungetc(c);
527 			break;
528 		}
529 		val = symget(buf);
530 		if (val == NULL) {
531 			yyerror("macro '%s' not defined", buf);
532 			return (findeol());
533 		}
534 		p = val + strlen(val) - 1;
535 		lungetc(DONE_EXPAND);
536 		while (p >= val) {
537 			lungetc((unsigned char)*p);
538 			p--;
539 		}
540 		lungetc(START_EXPAND);
541 		goto top;
542 	}
543 
544 	switch (c) {
545 	case '\'':
546 	case '"':
547 		quotec = c;
548 		while (1) {
549 			if ((c = lgetc(quotec)) == EOF)
550 				return (0);
551 			if (c == '\n') {
552 				file->lineno++;
553 				continue;
554 			} else if (c == '\\') {
555 				if ((next = lgetc(quotec)) == EOF)
556 					return (0);
557 				if (next == quotec || next == ' ' ||
558 				    next == '\t')
559 					c = next;
560 				else if (next == '\n') {
561 					file->lineno++;
562 					continue;
563 				} else
564 					lungetc(next);
565 			} else if (c == quotec) {
566 				*p = '\0';
567 				break;
568 			} else if (c == '\0') {
569 				yyerror("syntax error");
570 				return (findeol());
571 			}
572 			if (p + 1 >= buf + sizeof(buf) - 1) {
573 				yyerror("string too long");
574 				return (findeol());
575 			}
576 			*p++ = c;
577 		}
578 		yylval.v.string = strdup(buf);
579 		if (yylval.v.string == NULL)
580 			err(1, "%s", __func__);
581 		return (STRING);
582 	}
583 
584 #define allowed_to_end_number(x) \
585 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
586 
587 	if (c == '-' || isdigit(c)) {
588 		do {
589 			*p++ = c;
590 			if ((size_t)(p-buf) >= sizeof(buf)) {
591 				yyerror("string too long");
592 				return (findeol());
593 			}
594 		} while ((c = lgetc(0)) != EOF && isdigit(c));
595 		lungetc(c);
596 		if (p == buf + 1 && buf[0] == '-')
597 			goto nodigits;
598 		if (c == EOF || allowed_to_end_number(c)) {
599 			const char *errstr = NULL;
600 
601 			*p = '\0';
602 			yylval.v.number = strtonum(buf, LLONG_MIN,
603 			    LLONG_MAX, &errstr);
604 			if (errstr) {
605 				yyerror("\"%s\" invalid number: %s",
606 				    buf, errstr);
607 				return (findeol());
608 			}
609 			return (NUMBER);
610 		} else {
611 nodigits:
612 			while (p > buf + 1)
613 				lungetc((unsigned char)*--p);
614 			c = (unsigned char)*--p;
615 			if (c == '-')
616 				return (c);
617 		}
618 	}
619 
620 #define allowed_in_string(x) \
621 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
622 	x != '{' && x != '}' && \
623 	x != '!' && x != '=' && x != '#' && \
624 	x != ','))
625 
626 	if (isalnum(c) || c == ':' || c == '_') {
627 		do {
628 			*p++ = c;
629 			if ((size_t)(p-buf) >= sizeof(buf)) {
630 				yyerror("string too long");
631 				return (findeol());
632 			}
633 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
634 		lungetc(c);
635 		*p = '\0';
636 		if ((token = lookup(buf)) == STRING)
637 			if ((yylval.v.string = strdup(buf)) == NULL)
638 				err(1, "%s", __func__);
639 		return (token);
640 	}
641 	if (c == '\n') {
642 		yylval.lineno = file->lineno;
643 		file->lineno++;
644 	}
645 	if (c == EOF)
646 		return (0);
647 	return (c);
648 }
649 
650 struct file *
pushfile(const char * name,int secret)651 pushfile(const char *name, int secret)
652 {
653 	struct file	*nfile;
654 
655 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
656 		warn("%s", __func__);
657 		return (NULL);
658 	}
659 	if ((nfile->name = strdup(name)) == NULL) {
660 		warn("%s", __func__);
661 		free(nfile);
662 		return (NULL);
663 	}
664 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
665 		warn("%s: %s", __func__, nfile->name);
666 		free(nfile->name);
667 		free(nfile);
668 		return (NULL);
669 	}
670 	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
671 	nfile->ungetsize = 16;
672 	nfile->ungetbuf = malloc(nfile->ungetsize);
673 	if (nfile->ungetbuf == NULL) {
674 		warn("%s", __func__);
675 		fclose(nfile->stream);
676 		free(nfile->name);
677 		free(nfile);
678 		return (NULL);
679 	}
680 	TAILQ_INSERT_TAIL(&files, nfile, entry);
681 	return (nfile);
682 }
683 
684 int
popfile(void)685 popfile(void)
686 {
687 	struct file	*prev;
688 
689 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
690 		prev->errors += file->errors;
691 
692 	TAILQ_REMOVE(&files, file, entry);
693 	fclose(file->stream);
694 	free(file->name);
695 	free(file->ungetbuf);
696 	free(file);
697 	file = prev;
698 	return (file ? 0 : EOF);
699 }
700 
701 struct iscsi_config *
parse_config(char * filename)702 parse_config(char *filename)
703 {
704 	struct sym	*sym, *next;
705 
706 	file = pushfile(filename, 1);
707 	if (file == NULL)
708 		return (NULL);
709 	topfile = file;
710 
711 	conf = calloc(1, sizeof(struct iscsi_config));
712 	if (conf == NULL)
713 		return (NULL);
714 	SIMPLEQ_INIT(&conf->sessions);
715 
716 	yyparse();
717 	errors = file->errors;
718 	popfile();
719 
720 	/* Free macros and check which have not been used. */
721 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
722 		if (!sym->persist) {
723 			free(sym->nam);
724 			free(sym->val);
725 			TAILQ_REMOVE(&symhead, sym, entry);
726 			free(sym);
727 		}
728 	}
729 
730 	if (errors) {
731 		clear_config(conf);
732 		return (NULL);
733 	}
734 
735 	return (conf);
736 }
737 
738 int
cmdline_symset(char * s)739 cmdline_symset(char *s)
740 {
741 	char	*sym, *val;
742 	int	ret;
743 
744 	if ((val = strrchr(s, '=')) == NULL)
745 		return (-1);
746 	sym = strndup(s, val - s);
747 	if (sym == NULL)
748 		errx(1, "%s: strndup", __func__);
749 	ret = symset(sym, val + 1, 1);
750 	free(sym);
751 
752 	return (ret);
753 }
754 
755 char *
symget(const char * nam)756 symget(const char *nam)
757 {
758 	struct sym	*sym;
759 
760 	TAILQ_FOREACH(sym, &symhead, entry) {
761 		if (strcmp(nam, sym->nam) == 0) {
762 			sym->used = 1;
763 			return (sym->val);
764 		}
765 	}
766 	return (NULL);
767 }
768 
769 int
symset(const char * nam,const char * val,int persist)770 symset(const char *nam, const char *val, int persist)
771 {
772 	struct sym	*sym;
773 
774 	TAILQ_FOREACH(sym, &symhead, entry) {
775 		if (strcmp(nam, sym->nam) == 0)
776 			break;
777 	}
778 
779 	if (sym != NULL) {
780 		if (sym->persist == 1)
781 			return (0);
782 		else {
783 			free(sym->nam);
784 			free(sym->val);
785 			TAILQ_REMOVE(&symhead, sym, entry);
786 			free(sym);
787 		}
788 	}
789 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
790 		return (-1);
791 
792 	sym->nam = strdup(nam);
793 	if (sym->nam == NULL) {
794 		free(sym);
795 		return (-1);
796 	}
797 	sym->val = strdup(val);
798 	if (sym->val == NULL) {
799 		free(sym->nam);
800 		free(sym);
801 		return (-1);
802 	}
803 	sym->used = 0;
804 	sym->persist = persist;
805 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
806 	return (0);
807 }
808 
809 void
clear_config(struct iscsi_config * c)810 clear_config(struct iscsi_config *c)
811 {
812 	struct session_ctlcfg *s;
813 
814 	while ((s = SIMPLEQ_FIRST(&c->sessions))) {
815 		SIMPLEQ_REMOVE_HEAD(&c->sessions, entry);
816 		free(s->session.TargetName);
817 		free(s->session.InitiatorName);
818 		free(s);
819 	}
820 
821 	free(c);
822 }
823