geom_xml2tree.c revision 233646
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: head/lib/libgeom/geom_xml2tree.c 233646 2012-03-29 03:13:43Z jmallett $
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
49struct 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
61static void *
62internalize_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
108static void
109StartElement(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
221static void
222EndElement(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
345static void
346CharData(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
363struct gident *
364geom_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
379int
380geom_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
464int
465geom_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
478static void
479delete_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
494void
495geom_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