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