1 /*
2  * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include <ctype.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "istgt.h"
38 #include "istgt_misc.h"
39 #include "istgt_conf.h"
40 
41 //#define CF_DELIM " \t,;"
42 #define CF_DELIM " \t"
43 
44 static void istgt_free_all_cf_section(CF_SECTION *sp);
45 static void istgt_free_all_cf_item(CF_ITEM *ip);
46 static void istgt_free_all_cf_value(CF_VALUE *vp);
47 static void istgt_append_cf_item(CF_SECTION *sp, CF_ITEM *ip);
48 static void istgt_append_cf_value(CF_ITEM *ip, CF_VALUE *vp);
49 
50 CONFIG *
istgt_allocate_config(void)51 istgt_allocate_config(void)
52 {
53 	CONFIG *cp;
54 
55 	cp = xmalloc(sizeof *cp);
56 	memset(cp, 0, sizeof *cp);
57 	cp->file = NULL;
58 	cp->section = NULL;
59 
60 	return cp;
61 }
62 
63 void
istgt_free_config(CONFIG * cp)64 istgt_free_config(CONFIG *cp)
65 {
66 	if (cp == NULL)
67 		return;
68 	if (cp->section != NULL) {
69 		istgt_free_all_cf_section(cp->section);
70 	}
71 	xfree(cp->file);
72 	xfree(cp);
73 }
74 
75 static CF_SECTION *
istgt_allocate_cf_section(void)76 istgt_allocate_cf_section(void)
77 {
78 	CF_SECTION *sp;
79 
80 	sp = xmalloc(sizeof *sp);
81 	memset(sp, 0, sizeof *sp);
82 	sp->next = NULL;
83 	sp->item = NULL;
84 
85 	return sp;
86 }
87 
88 static void
istgt_free_cf_section(CF_SECTION * sp)89 istgt_free_cf_section(CF_SECTION *sp)
90 {
91 	if (sp == NULL)
92 		return;
93 	if (sp->item) {
94 		istgt_free_all_cf_item(sp->item);
95 	}
96 	xfree(sp->name);
97 	xfree(sp);
98 }
99 
100 static void
istgt_free_all_cf_section(CF_SECTION * sp)101 istgt_free_all_cf_section(CF_SECTION *sp)
102 {
103 	CF_SECTION *next;
104 
105 	if (sp == NULL)
106 		return;
107 	while (sp != NULL) {
108 		next = sp->next;
109 		istgt_free_cf_section(sp);
110 		sp = next;
111 	}
112 }
113 
114 static CF_ITEM *
istgt_allocate_cf_item(void)115 istgt_allocate_cf_item(void)
116 {
117 	CF_ITEM *ip;
118 
119 	ip = xmalloc(sizeof *ip);
120 	memset(ip, 0, sizeof *ip);
121 	ip->next = NULL;
122 	ip->key = NULL;
123 	ip->val = NULL;
124 
125 	return ip;
126 }
127 
128 static void
istgt_free_cf_item(CF_ITEM * ip)129 istgt_free_cf_item(CF_ITEM *ip)
130 {
131 	if (ip == NULL)
132 		return;
133 	if (ip->val != NULL) {
134 		istgt_free_all_cf_value(ip->val);
135 	}
136 	xfree(ip->key);
137 	xfree(ip);
138 }
139 
140 static void
istgt_free_all_cf_item(CF_ITEM * ip)141 istgt_free_all_cf_item(CF_ITEM *ip)
142 {
143 	CF_ITEM *next;
144 
145 	if (ip == NULL)
146 		return;
147 	while (ip != NULL) {
148 		next = ip->next;
149 		istgt_free_cf_item(ip);
150 		ip = next;
151 	}
152 }
153 
154 static CF_VALUE *
istgt_allocate_cf_value(void)155 istgt_allocate_cf_value(void)
156 {
157 	CF_VALUE *vp;
158 
159 	vp = xmalloc(sizeof *vp);
160 	memset(vp, 0, sizeof *vp);
161 	vp->next = NULL;
162 	vp->value = NULL;
163 
164 	return vp;
165 }
166 
167 static void
istgt_free_cf_value(CF_VALUE * vp)168 istgt_free_cf_value(CF_VALUE *vp)
169 {
170 	if (vp == NULL)
171 		return;
172 	xfree(vp->value);
173 	xfree(vp);
174 }
175 
176 static void
istgt_free_all_cf_value(CF_VALUE * vp)177 istgt_free_all_cf_value(CF_VALUE *vp)
178 {
179 	CF_VALUE *next;
180 
181 	if (vp == NULL)
182 		return;
183 	while (vp != NULL) {
184 		next = vp->next;
185 		istgt_free_cf_value(vp);
186 		vp = next;
187 	}
188 }
189 
190 void
istgt_copy_cf_item(CF_SECTION * sp_dst,CF_SECTION * sp_src)191 istgt_copy_cf_item(CF_SECTION *sp_dst, CF_SECTION *sp_src)
192 {
193 	CF_ITEM *ip, *ip_old;
194 	CF_VALUE *vp, *vp_old;
195 
196 	istgt_free_all_cf_item(sp_dst->item);
197 	sp_dst->item = NULL;
198 
199 	ip_old = sp_src->item;
200 	while (ip_old != NULL) {
201 		ip = istgt_allocate_cf_item();
202 		istgt_append_cf_item(sp_dst, ip);
203 		ip->key = xstrdup(ip_old->key);
204 		ip->val = NULL;
205 
206 		vp_old = ip_old->val;
207 		while (vp_old != NULL) {
208 			vp = istgt_allocate_cf_value();
209 			istgt_append_cf_value(ip, vp);
210 			vp->value = xstrdup(vp_old->value);
211 
212 			vp_old = vp_old->next;
213 		}
214 		ip_old = ip_old->next;
215 	}
216 }
217 
218 CF_SECTION *
istgt_find_cf_section(CONFIG * cp,const char * name)219 istgt_find_cf_section(CONFIG *cp, const char *name)
220 {
221 	CF_SECTION *sp;
222 
223 	if (name == NULL || name[0] == '\0')
224 		return NULL;
225 
226 	for (sp = cp->section; sp != NULL; sp = sp->next) {
227 		if (sp->name != NULL && sp->name[0] == name[0]
228 			&& strcasecmp(sp->name, name) == 0) {
229 			return sp;
230 		}
231 	}
232 
233 	return NULL;
234 }
235 
236 static void
istgt_append_cf_section(CONFIG * cp,CF_SECTION * sp)237 istgt_append_cf_section(CONFIG *cp, CF_SECTION *sp)
238 {
239 	CF_SECTION *last;
240 
241 	if (cp == NULL)
242 		return;
243 	if (cp->section == NULL) {
244 		cp->section = sp;
245 		return;
246 	}
247 	for (last = cp->section; last->next != NULL; last = last->next)
248 		;
249 	last->next = sp;
250 }
251 
252 CF_ITEM *
istgt_find_cf_nitem(CF_SECTION * sp,const char * key,int idx)253 istgt_find_cf_nitem(CF_SECTION *sp, const char *key, int idx)
254 {
255 	CF_ITEM *ip;
256 	int i;
257 
258 	if (key == NULL || key[0] == '\0')
259 		return NULL;
260 
261 	i = 0;
262 	for (ip = sp->item; ip != NULL; ip = ip->next) {
263 		if (ip->key != NULL && ip->key[0] == key[0]
264 			&& strcasecmp(ip->key, key) == 0) {
265 			if (i == idx) {
266 				return ip;
267 			}
268 			i++;
269 		}
270 	}
271 
272 	return NULL;
273 }
274 
275 CF_ITEM *
istgt_find_cf_item(CF_SECTION * sp,const char * key)276 istgt_find_cf_item(CF_SECTION *sp, const char *key)
277 {
278 	return istgt_find_cf_nitem(sp, key, 0);
279 }
280 
281 static void
istgt_append_cf_item(CF_SECTION * sp,CF_ITEM * ip)282 istgt_append_cf_item(CF_SECTION *sp, CF_ITEM *ip)
283 {
284 	CF_ITEM *last;
285 
286 	if (sp == NULL)
287 		return;
288 	if (sp->item == NULL) {
289 		sp->item = ip;
290 		return;
291 	}
292 	for (last = sp->item; last->next != NULL; last = last->next)
293 		;
294 	last->next = ip;
295 }
296 
297 static void
istgt_append_cf_value(CF_ITEM * ip,CF_VALUE * vp)298 istgt_append_cf_value(CF_ITEM *ip, CF_VALUE *vp)
299 {
300 	CF_VALUE *last;
301 
302 	if (ip == NULL)
303 		return;
304 	if (ip->val == NULL) {
305 		ip->val = vp;
306 		return;
307 	}
308 	for (last = ip->val; last->next != NULL; last = last->next)
309 		;
310 	last->next = vp;
311 }
312 
313 static void
istgt_set_cf_section_type(CF_SECTION * sp)314 istgt_set_cf_section_type(CF_SECTION *sp)
315 {
316 	static struct cfst_table_t {
317 		const char *name;
318 		CF_SECTION_TYPE type;
319 	} cfst_table[] = {
320 		{ "Global", ST_GLOBAL },
321 		{ "UnitControl", ST_UNITCONTROL },
322 		{ "PortalGroup", ST_PORTALGROUP },
323 		{ "InitiatorGroup", ST_INITIATORGROUP },
324 		{ "LogicalUnit", ST_LOGICAL_UNIT },
325 		{ "AuthGroup", ST_AUTHGROUP },
326 		{ NULL, ST_INVALID },
327 	};
328 	int i;
329 
330 	if (sp == NULL || sp->name == NULL)
331 		return;
332 	for (i = 0; cfst_table[i].name != NULL; i++) {
333 		if (sp->name[0] == cfst_table[i].name[0]
334 			&& strncasecmp(sp->name, cfst_table[i].name,
335 						   strlen(cfst_table[i].name)) == 0) {
336 			sp->type = cfst_table[i].type;
337 			return;
338 		}
339 	}
340 	sp->type = ST_NONE;
341 }
342 
343 static int
parse_line(CONFIG * cp,char * lp)344 parse_line(CONFIG *cp, char *lp)
345 {
346 	CF_SECTION *sp;
347 	CF_ITEM *ip;
348 	CF_VALUE *vp;
349 	char *arg;
350 	char *key;
351 	char *val;
352 	char *p;
353 	int num;
354 
355 	arg = trim_string(lp);
356 	if (arg[0] == '[') {
357 		/* section */
358 		arg++;
359 		key = strsepq(&arg, "]");
360 		if (key == NULL || arg != NULL) {
361 			fprintf(stderr, "broken section\n");
362 			return -1;
363 		}
364 		/* determine section number */
365 		for (p = key; *p != '\0' && !isdigit((int) *p); p++)
366 			;
367 		if (*p != '\0') {
368 			num = (int)strtol(p, NULL, 10);
369 		} else {
370 			num = 0;
371 		}
372 
373 		sp = istgt_find_cf_section(cp, key);
374 		if (sp == NULL) {
375 			sp = istgt_allocate_cf_section();
376 			istgt_append_cf_section(cp, sp);
377 		}
378 		cp->current_section = sp;
379 		sp->name = xstrdup(key);
380 		sp->num = num;
381 		istgt_set_cf_section_type(sp);
382 	} else {
383 		/* parameters */
384 		sp = cp->current_section;
385 		if (sp == NULL) {
386 			fprintf(stderr, "unknown section\n");
387 			return -1;
388 		}
389 		key = strsepq(&arg, CF_DELIM);
390 		if (key == NULL) {
391 			fprintf(stderr, "broken key\n");
392 			return -1;
393 		}
394 
395 		ip = istgt_allocate_cf_item();
396 		istgt_append_cf_item(sp, ip);
397 		ip->key = xstrdup(key);
398 		ip->val = NULL;
399 		if (arg != NULL) {
400 			/* key has value(s) */
401 			while (arg != NULL) {
402 				val = strsepq(&arg, CF_DELIM);
403 				vp = istgt_allocate_cf_value();
404 				istgt_append_cf_value(ip, vp);
405 				vp->value = xstrdup(val);
406 			}
407 		}
408 	}
409 
410 	return 0;
411 }
412 
413 static char *
fgets_line(FILE * fp)414 fgets_line (FILE *fp)
415 {
416 	char *dst, *p;
417 	size_t total, len;
418 
419 	dst = p = xmalloc(MAX_TMPBUF);
420 	dst[0] = '\0';
421 	total = 0;
422 
423 	while (fgets(p, MAX_TMPBUF, fp) != NULL) {
424 		len = strlen(p);
425 		total += len;
426 		if (len + 1 < MAX_TMPBUF || dst[total - 1] == '\n') {
427 			return xrealloc(dst, total + 1);
428 		}
429 		dst = xrealloc (dst, total + MAX_TMPBUF);
430 		p = dst + total;
431 	}
432 
433 	if (feof(fp) && total != 0) {
434 		dst = xrealloc(dst, total + 2);
435 		dst[total] = '\n';
436 		dst[total + 1] = '\0';
437 		return dst;
438 	}
439 
440 	xfree(dst);
441 
442 	return NULL;
443 }
444 
445 int
istgt_read_config(CONFIG * cp,const char * file)446 istgt_read_config(CONFIG *cp, const char *file)
447 {
448 	FILE *fp;
449 	char *lp, *p;
450 	char *lp2, *q;
451 	int line;
452 	int n, n2;
453 
454 	if (file == NULL || file[0] == '\0')
455 		return -1;
456 	fp = fopen(file, "r");
457 	if (fp == NULL) {
458 		fprintf(stderr, "open error: %s\n", file);
459 		return -1;
460 	}
461 	cp->file = xstrdup(file);
462 
463 	line = 1;
464 	while ((lp = fgets_line(fp)) != NULL) {
465 		/* skip spaces */
466 		for (p = lp; *p != '\0' && isspace((int) *p); p++)
467 			;
468 		/* skip comment, empty line */
469 		if (p[0] == '#' || p[0] == '\0')
470 			goto next_line;
471 
472 		/* concatenate line end with '\' */
473 		n = strlen(p);
474 		while (n > 2 && p[n - 1] == '\n' && p[n - 2] == '\\') {
475 			n -= 2;
476 			lp2 = fgets_line(fp);
477 			if (lp2 == NULL)
478 				break;
479 			line++;
480 			n2 = strlen(lp2);
481 			q = xmalloc(n + n2 + 1);
482 			memcpy(q, p, n);
483 			memcpy(q + n, lp2, n2);
484 			q[n + n2] = '\0';
485 			xfree(lp2);
486 			xfree(lp);
487 			p = lp = q;
488 			n += n2;
489 		}
490 
491 		/* parse one line */
492 		if (parse_line(cp, p) < 0) {
493 			fprintf(stderr, "parse error at line %d of %s\n", line, cp->file);
494 		}
495 	next_line:
496 		line++;
497 		xfree(lp);
498 	}
499 
500 	fclose(fp);
501 	return 0;
502 }
503 
504 int
istgt_print_config(CONFIG * cp)505 istgt_print_config(CONFIG *cp)
506 {
507 	CF_SECTION *sp;
508 	CF_ITEM *ip;
509 	CF_VALUE *vp;
510 
511 	if (cp == NULL)
512 		return -1;
513 
514 	/* empty config? */
515 	sp = cp->section;
516 	if (sp == NULL)
517 		return 0;
518 
519 	while (sp != NULL) {
520 		printf("Section: %s\n", sp->name);
521 		ip = sp->item;
522 		while (ip != NULL) {
523 			printf("  Item: %s ", ip->key);
524 			vp = ip->val;
525 			while (vp != NULL) {
526 				printf("Val: %s ", vp->value);
527 				vp = vp->next;
528 			}
529 			printf("\n");
530 			ip = ip->next;
531 		}
532 		sp = sp->next;
533 	}
534 
535 	return 0;
536 }
537