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