xref: /freebsd/lib/libgeom/geom_xml2tree.c (revision cf771f223b1a69e11fd1b70b0a274a73fe844335)
1 /*-
2  * Copyright (c) 2003 Poul-Henning Kamp
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  * 3. The names of the authors may not be used to endorse or promote
14  *    products derived from this software without specific prior written
15  *    permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 
32 #include <stdio.h>
33 #include <inttypes.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <ctype.h>
40 #include <sys/stat.h>
41 #include <sys/mman.h>
42 #include <sys/queue.h>
43 #include <sys/sbuf.h>
44 #include <sys/sysctl.h>
45 #include <err.h>
46 #include <bsdxml.h>
47 #include <libgeom.h>
48 
49 struct mystate {
50 	struct gmesh		*mesh;
51 	struct gclass		*class;
52 	struct ggeom		*geom;
53 	struct gprovider	*provider;
54 	struct gconsumer	*consumer;
55 	int			level;
56 	struct sbuf		*sbuf[20];
57 	struct gconf		*config;
58 	unsigned		nident;
59 };
60 
61 static void *
62 internalize_ident(struct mystate *mt, const char *element, const char *str)
63 {
64 	struct gident *gip;
65 	unsigned i;
66 
67 	if (mt->nident != 0 && mt->mesh->lg_ident == NULL) {
68 		warn("Cannot continue due to previous memory exhaustion.");
69 		return (NULL);
70 	}
71 
72 	for (i = 0; i < mt->nident; i++) {
73 		if (strcmp(mt->mesh->lg_ident[i].lg_id, str) != 0)
74 			continue;
75 		return ((void *)(uintptr_t)(i + 1));
76 	}
77 
78 	i = mt->nident;
79 	mt->nident++;
80 	mt->mesh->lg_ident = reallocf(mt->mesh->lg_ident, (mt->nident + 1) * sizeof mt->mesh->lg_ident[0]);
81 	if (mt->mesh->lg_ident == NULL) {
82 		warn("Cannot allocate memory during processing of '%s' "
83 		    "element for identifier '%s'", element, str);
84 		return (NULL);
85 	}
86 
87 	gip = &mt->mesh->lg_ident[i];
88 	gip->lg_id = strdup(str);
89 	if (gip->lg_id == NULL) {
90 		free(mt->mesh->lg_ident);
91 		mt->mesh->lg_ident = NULL;
92 		warn("Cannot allocate memory during processing of '%s' "
93 		    "element for identifier '%s'", element, str);
94 		return (NULL);
95 	}
96 	gip->lg_ptr = NULL;
97 	gip->lg_what = ISUNRESOLVED;
98 
99 	/* Terminator entry.  */
100 	gip = &mt->mesh->lg_ident[i + 1];
101 	gip->lg_id = NULL;
102 	gip->lg_ptr = NULL;
103 	gip->lg_what = ISUNRESOLVED;
104 
105 	return ((void *)(uintptr_t)(i + 1));
106 }
107 
108 static void
109 StartElement(void *userData, const char *name, const char **attr)
110 {
111 	struct mystate *mt;
112 	void *id;
113 	void *ref;
114 	int i;
115 
116 	mt = userData;
117 	mt->level++;
118 	mt->sbuf[mt->level] = sbuf_new_auto();
119 	id = NULL;
120 	ref = NULL;
121 	for (i = 0; attr[i] != NULL; i += 2) {
122 		if (!strcmp(attr[i], "id")) {
123 			id = internalize_ident(mt, name, attr[i + 1]);
124 		} else if (!strcmp(attr[i], "ref")) {
125 			ref = internalize_ident(mt, name, attr[i + 1]);
126 		} else
127 			printf("%*.*s[%s = %s]\n",
128 			    mt->level + 1, mt->level + 1, "",
129 			    attr[i], attr[i + 1]);
130 	}
131 	if (!strcmp(name, "class") && mt->class == NULL) {
132 		mt->class = calloc(1, sizeof *mt->class);
133 		if (mt->class == NULL) {
134 			warn("Cannot allocate memory during processing of '%s' "
135 			    "element", name);
136 			return;
137 		}
138 		mt->class->lg_id = id;
139 		LIST_INSERT_HEAD(&mt->mesh->lg_class, mt->class, lg_class);
140 		LIST_INIT(&mt->class->lg_geom);
141 		LIST_INIT(&mt->class->lg_config);
142 		return;
143 	}
144 	if (!strcmp(name, "geom") && mt->geom == NULL) {
145 		mt->geom = calloc(1, sizeof *mt->geom);
146 		if (mt->geom == NULL) {
147 			warn("Cannot allocate memory during processing of '%s' "
148 			    "element", name);
149 			return;
150 		}
151 		mt->geom->lg_id = id;
152 		LIST_INSERT_HEAD(&mt->class->lg_geom, mt->geom, lg_geom);
153 		LIST_INIT(&mt->geom->lg_provider);
154 		LIST_INIT(&mt->geom->lg_consumer);
155 		LIST_INIT(&mt->geom->lg_config);
156 		return;
157 	}
158 	if (!strcmp(name, "class") && mt->geom != NULL) {
159 		mt->geom->lg_class = ref;
160 		return;
161 	}
162 	if (!strcmp(name, "consumer") && mt->consumer == NULL) {
163 		mt->consumer = calloc(1, sizeof *mt->consumer);
164 		if (mt->consumer == NULL) {
165 			warn("Cannot allocate memory during processing of '%s' "
166 			    "element", name);
167 			return;
168 		}
169 		mt->consumer->lg_id = id;
170 		LIST_INSERT_HEAD(&mt->geom->lg_consumer, mt->consumer,
171 		    lg_consumer);
172 		LIST_INIT(&mt->consumer->lg_config);
173 		return;
174 	}
175 	if (!strcmp(name, "geom") && mt->consumer != NULL) {
176 		mt->consumer->lg_geom = ref;
177 		return;
178 	}
179 	if (!strcmp(name, "provider") && mt->consumer != NULL) {
180 		mt->consumer->lg_provider = ref;
181 		return;
182 	}
183 	if (!strcmp(name, "provider") && mt->provider == NULL) {
184 		mt->provider = calloc(1, sizeof *mt->provider);
185 		if (mt->provider == NULL) {
186 			warn("Cannot allocate memory during processing of '%s' "
187 			    "element", name);
188 			return;
189 		}
190 		mt->provider->lg_id = id;
191 		LIST_INSERT_HEAD(&mt->geom->lg_provider, mt->provider,
192 		    lg_provider);
193 		LIST_INIT(&mt->provider->lg_consumers);
194 		LIST_INIT(&mt->provider->lg_config);
195 		return;
196 	}
197 	if (!strcmp(name, "geom") && mt->provider != NULL) {
198 		mt->provider->lg_geom = ref;
199 		return;
200 	}
201 	if (!strcmp(name, "config")) {
202 		if (mt->provider != NULL) {
203 			mt->config = &mt->provider->lg_config;
204 			return;
205 		}
206 		if (mt->consumer != NULL) {
207 			mt->config = &mt->consumer->lg_config;
208 			return;
209 		}
210 		if (mt->geom != NULL) {
211 			mt->config = &mt->geom->lg_config;
212 			return;
213 		}
214 		if (mt->class != NULL) {
215 			mt->config = &mt->class->lg_config;
216 			return;
217 		}
218 	}
219 }
220 
221 static void
222 EndElement(void *userData, const char *name)
223 {
224 	struct mystate *mt;
225 	struct gconfig *gc;
226 	char *p;
227 
228 	mt = userData;
229 	sbuf_finish(mt->sbuf[mt->level]);
230 	p = strdup(sbuf_data(mt->sbuf[mt->level]));
231 	if (p == NULL) {
232 		warn("Cannot allocate memory during processing of '%s' "
233 		    "element", name);
234 		return;
235 	}
236 	sbuf_delete(mt->sbuf[mt->level]);
237 	mt->sbuf[mt->level] = NULL;
238 	mt->level--;
239 	if (strlen(p) == 0) {
240 		free(p);
241 		p = NULL;
242 	}
243 
244 	if (!strcmp(name, "name")) {
245 		if (mt->provider != NULL) {
246 			mt->provider->lg_name = p;
247 			return;
248 		} else if (mt->geom != NULL) {
249 			mt->geom->lg_name = p;
250 			return;
251 		} else if (mt->class != NULL) {
252 			mt->class->lg_name = p;
253 			return;
254 		}
255 	}
256 	if (!strcmp(name, "rank") && mt->geom != NULL) {
257 		mt->geom->lg_rank = strtoul(p, NULL, 0);
258 		free(p);
259 		return;
260 	}
261 	if (!strcmp(name, "mode") && mt->provider != NULL) {
262 		mt->provider->lg_mode = p;
263 		return;
264 	}
265 	if (!strcmp(name, "mode") && mt->consumer != NULL) {
266 		mt->consumer->lg_mode = p;
267 		return;
268 	}
269 	if (!strcmp(name, "mediasize") && mt->provider != NULL) {
270 		mt->provider->lg_mediasize = strtoumax(p, NULL, 0);
271 		free(p);
272 		return;
273 	}
274 	if (!strcmp(name, "sectorsize") && mt->provider != NULL) {
275 		mt->provider->lg_sectorsize = strtoul(p, NULL, 0);
276 		free(p);
277 		return;
278 	}
279 	if (!strcmp(name, "stripesize") && mt->provider != NULL) {
280 		mt->provider->lg_stripesize = strtoumax(p, NULL, 0);
281 		free(p);
282 		return;
283 	}
284 	if (!strcmp(name, "stripeoffset") && mt->provider != NULL) {
285 		mt->provider->lg_stripeoffset = strtoumax(p, NULL, 0);
286 		free(p);
287 		return;
288 	}
289 
290 	if (!strcmp(name, "config")) {
291 		mt->config = NULL;
292 		return;
293 	}
294 
295 	if (mt->config != NULL) {
296 		gc = calloc(1, sizeof *gc);
297 		if (gc == NULL) {
298 			warn("Cannot allocate memory during processing of '%s' "
299 			    "element", name);
300 			return;
301 		}
302 		gc->lg_name = strdup(name);
303 		if (gc->lg_name == NULL) {
304 			warn("Cannot allocate memory during processing of '%s' "
305 			    "element", name);
306 			return;
307 		}
308 		gc->lg_val = p;
309 		LIST_INSERT_HEAD(mt->config, gc, lg_config);
310 		return;
311 	}
312 
313 	if (p != NULL) {
314 		printf("Unexpected XML: name=%s data=\"%s\"\n", name, p);
315 		free(p);
316 	}
317 
318 	if (!strcmp(name, "consumer") && mt->consumer != NULL) {
319 		mt->consumer = NULL;
320 		return;
321 	}
322 	if (!strcmp(name, "provider") && mt->provider != NULL) {
323 		mt->provider = NULL;
324 		return;
325 	}
326 	if (!strcmp(name, "geom") && mt->consumer != NULL) {
327 		return;
328 	}
329 	if (!strcmp(name, "geom") && mt->provider != NULL) {
330 		return;
331 	}
332 	if (!strcmp(name, "geom") && mt->geom != NULL) {
333 		mt->geom = NULL;
334 		return;
335 	}
336 	if (!strcmp(name, "class") && mt->geom != NULL) {
337 		return;
338 	}
339 	if (!strcmp(name, "class") && mt->class != NULL) {
340 		mt->class = NULL;
341 		return;
342 	}
343 }
344 
345 static void
346 CharData(void *userData , const XML_Char *s , int len)
347 {
348 	struct mystate *mt;
349 	const char *b, *e;
350 
351 	mt = userData;
352 
353 	b = s;
354 	e = s + len - 1;
355 	while (isspace(*b) && b < e)
356 		b++;
357 	while (isspace(*e) && e > b)
358 		e--;
359 	if (e != b || (*b && !isspace(*b)))
360 		sbuf_bcat(mt->sbuf[mt->level], b, e - b + 1);
361 }
362 
363 struct gident *
364 geom_lookupid(struct gmesh *gmp, const void *id)
365 {
366 	unsigned i;
367 
368 	if (gmp->lg_ident == NULL)
369 		return (NULL);
370 
371 	for (i = 0; gmp->lg_ident[i].lg_id != NULL; i++) {
372 		if (i + 1 != (unsigned)(uintptr_t)id)
373 			continue;
374 		return (&gmp->lg_ident[i]);
375 	}
376 	return (NULL);
377 }
378 
379 int
380 geom_xml2tree(struct gmesh *gmp, char *p)
381 {
382 	XML_Parser parser;
383 	struct mystate *mt;
384 	struct gclass *cl;
385 	struct ggeom *ge;
386 	struct gprovider *pr;
387 	struct gconsumer *co;
388 	struct gident *gip;
389 	int i;
390 
391 	memset(gmp, 0, sizeof *gmp);
392 	LIST_INIT(&gmp->lg_class);
393 	parser = XML_ParserCreate(NULL);
394 	if (parser == NULL)
395 		return (ENOMEM);
396 	mt = calloc(1, sizeof *mt);
397 	if (mt == NULL) {
398 		XML_ParserFree(parser);
399 		return (ENOMEM);
400 	}
401 	mt->mesh = gmp;
402 	XML_SetUserData(parser, mt);
403 	XML_SetElementHandler(parser, StartElement, EndElement);
404 	XML_SetCharacterDataHandler(parser, CharData);
405 	i = XML_Parse(parser, p, strlen(p), 1);
406 	XML_ParserFree(parser);
407 	if (i != 1) {
408 		free(mt);
409 		return (-1);
410 	}
411 	if (gmp->lg_ident == NULL && mt->nident != 0) {
412 		free(mt);
413 		return (ENOMEM);
414 	}
415 	free(mt);
416 	/* Collect all identifiers */
417 	LIST_FOREACH(cl, &gmp->lg_class, lg_class) {
418 		gip = geom_lookupid(gmp, cl->lg_id);
419 		gip->lg_ptr = cl;
420 		gip->lg_what = ISCLASS;
421 
422 		LIST_FOREACH(ge, &cl->lg_geom, lg_geom) {
423 			gip = geom_lookupid(gmp, ge->lg_id);
424 			gip->lg_ptr = ge;
425 			gip->lg_what = ISGEOM;
426 			LIST_FOREACH(pr, &ge->lg_provider, lg_provider) {
427 				gip = geom_lookupid(gmp, pr->lg_id);
428 				gip->lg_ptr = pr;
429 				gip->lg_what = ISPROVIDER;
430 			}
431 			LIST_FOREACH(co, &ge->lg_consumer, lg_consumer) {
432 				gip = geom_lookupid(gmp, co->lg_id);
433 				gip->lg_ptr = co;
434 				gip->lg_what = ISCONSUMER;
435 			}
436 		}
437 	}
438 	/* Substitute all identifiers */
439 	LIST_FOREACH(cl, &gmp->lg_class, lg_class) {
440 		LIST_FOREACH(ge, &cl->lg_geom, lg_geom) {
441 			ge->lg_class =
442 			    geom_lookupid(gmp, ge->lg_class)->lg_ptr;
443 			LIST_FOREACH(pr, &ge->lg_provider, lg_provider) {
444 				pr->lg_geom =
445 				    geom_lookupid(gmp, pr->lg_geom)->lg_ptr;
446 			}
447 			LIST_FOREACH(co, &ge->lg_consumer, lg_consumer) {
448 				co->lg_geom =
449 				    geom_lookupid(gmp, co->lg_geom)->lg_ptr;
450 				if (co->lg_provider != NULL) {
451 					co->lg_provider =
452 					    geom_lookupid(gmp,
453 						co->lg_provider)->lg_ptr;
454 					LIST_INSERT_HEAD(
455 					    &co->lg_provider->lg_consumers,
456 					    co, lg_consumers);
457 				}
458 			}
459 		}
460 	}
461 	return (0);
462 }
463 
464 int
465 geom_gettree(struct gmesh *gmp)
466 {
467 	char *p;
468 	int error;
469 
470 	p = geom_getxml();
471 	if (p == NULL)
472 		return (errno);
473 	error = geom_xml2tree(gmp, p);
474 	free(p);
475 	return (error);
476 }
477 
478 static void
479 delete_config(struct gconf *gp)
480 {
481 	struct gconfig *cf;
482 
483 	for (;;) {
484 		cf = LIST_FIRST(gp);
485 		if (cf == NULL)
486 			return;
487 		LIST_REMOVE(cf, lg_config);
488 		free(cf->lg_name);
489 		free(cf->lg_val);
490 		free(cf);
491 	}
492 }
493 
494 void
495 geom_deletetree(struct gmesh *gmp)
496 {
497 	struct gclass *cl;
498 	struct ggeom *ge;
499 	struct gprovider *pr;
500 	struct gconsumer *co;
501 	unsigned i;
502 
503 	for (i = 0; gmp->lg_ident[i].lg_id != NULL; i++)
504 		free(gmp->lg_ident[i].lg_id);
505 	free(gmp->lg_ident);
506 	gmp->lg_ident = NULL;
507 	for (;;) {
508 		cl = LIST_FIRST(&gmp->lg_class);
509 		if (cl == NULL)
510 			break;
511 		LIST_REMOVE(cl, lg_class);
512 		delete_config(&cl->lg_config);
513 		if (cl->lg_name) free(cl->lg_name);
514 		for (;;) {
515 			ge = LIST_FIRST(&cl->lg_geom);
516 			if (ge == NULL)
517 				break;
518 			LIST_REMOVE(ge, lg_geom);
519 			delete_config(&ge->lg_config);
520 			if (ge->lg_name) free(ge->lg_name);
521 			for (;;) {
522 				pr = LIST_FIRST(&ge->lg_provider);
523 				if (pr == NULL)
524 					break;
525 				LIST_REMOVE(pr, lg_provider);
526 				delete_config(&pr->lg_config);
527 				if (pr->lg_name) free(pr->lg_name);
528 				if (pr->lg_mode) free(pr->lg_mode);
529 				free(pr);
530 			}
531 			for (;;) {
532 				co = LIST_FIRST(&ge->lg_consumer);
533 				if (co == NULL)
534 					break;
535 				LIST_REMOVE(co, lg_consumer);
536 				delete_config(&co->lg_config);
537 				if (co->lg_mode) free(co->lg_mode);
538 				free(co);
539 			}
540 			free(ge);
541 		}
542 		free(cl);
543 	}
544 }
545