1 /*	$NetBSD: libdm-report.c,v 1.1.1.3 2009/12/02 00:26:08 haad Exp $	*/
2 
3 /*
4  * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of the device-mapper userspace tools.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "dmlib.h"
19 
20 #include <ctype.h>
21 
22 /*
23  * Internal flags
24  */
25 #define RH_SORT_REQUIRED	0x00000100
26 #define RH_HEADINGS_PRINTED	0x00000200
27 
28 struct dm_report {
29 	struct dm_pool *mem;
30 
31 	uint32_t report_types;
32 	const char *output_field_name_prefix;
33 	const char *field_prefix;
34 	uint32_t flags;
35 	const char *separator;
36 
37 	uint32_t keys_count;
38 
39 	/* Ordered list of fields needed for this report */
40 	struct dm_list field_props;
41 
42 	/* Rows of report data */
43 	struct dm_list rows;
44 
45 	/* Array of field definitions */
46 	const struct dm_report_field_type *fields;
47 	const struct dm_report_object_type *types;
48 
49 	/* To store caller private data */
50 	void *private;
51 };
52 
53 /*
54  * Internal per-field flags
55  */
56 #define FLD_HIDDEN	0x00000100
57 #define FLD_SORT_KEY	0x00000200
58 #define FLD_ASCENDING	0x00000400
59 #define FLD_DESCENDING	0x00000800
60 
61 struct field_properties {
62 	struct dm_list list;
63 	uint32_t field_num;
64 	uint32_t sort_posn;
65 	int32_t width;
66 	const struct dm_report_object_type *type;
67 	uint32_t flags;
68 };
69 
70 /*
71  * Report data field
72  */
73 struct dm_report_field {
74 	struct dm_list list;
75 	struct field_properties *props;
76 
77 	const char *report_string;	/* Formatted ready for display */
78 	const void *sort_value;		/* Raw value for sorting */
79 };
80 
81 struct row {
82 	struct dm_list list;
83 	struct dm_report *rh;
84 	struct dm_list fields;			  /* Fields in display order */
85 	struct dm_report_field *(*sort_fields)[]; /* Fields in sort order */
86 };
87 
_find_type(struct dm_report * rh,uint32_t report_type)88 static const struct dm_report_object_type *_find_type(struct dm_report *rh,
89 						      uint32_t report_type)
90 {
91 	const struct dm_report_object_type *t;
92 
93 	for (t = rh->types; t->data_fn; t++)
94 		if (t->id == report_type)
95 			return t;
96 
97 	return NULL;
98 }
99 
100 /*
101  * Data-munging functions to prepare each data type for display and sorting
102  */
103 
dm_report_field_string(struct dm_report * rh,struct dm_report_field * field,const char ** data)104 int dm_report_field_string(struct dm_report *rh,
105 			   struct dm_report_field *field, const char **data)
106 {
107 	char *repstr;
108 
109 	if (!(repstr = dm_pool_strdup(rh->mem, *data))) {
110 		log_error("dm_report_field_string: dm_pool_strdup failed");
111 		return 0;
112 	}
113 
114 	field->report_string = repstr;
115 	field->sort_value = (const void *) field->report_string;
116 
117 	return 1;
118 }
119 
dm_report_field_int(struct dm_report * rh,struct dm_report_field * field,const int * data)120 int dm_report_field_int(struct dm_report *rh,
121 			struct dm_report_field *field, const int *data)
122 {
123 	const int value = *data;
124 	uint64_t *sortval;
125 	char *repstr;
126 
127 	if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
128 		log_error("dm_report_field_int: dm_pool_alloc failed");
129 		return 0;
130 	}
131 
132 	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
133 		log_error("dm_report_field_int: dm_pool_alloc failed");
134 		return 0;
135 	}
136 
137 	if (dm_snprintf(repstr, 12, "%d", value) < 0) {
138 		log_error("dm_report_field_int: int too big: %d", value);
139 		return 0;
140 	}
141 
142 	*sortval = (const uint64_t) value;
143 	field->sort_value = sortval;
144 	field->report_string = repstr;
145 
146 	return 1;
147 }
148 
dm_report_field_uint32(struct dm_report * rh,struct dm_report_field * field,const uint32_t * data)149 int dm_report_field_uint32(struct dm_report *rh,
150 			   struct dm_report_field *field, const uint32_t *data)
151 {
152 	const uint32_t value = *data;
153 	uint64_t *sortval;
154 	char *repstr;
155 
156 	if (!(repstr = dm_pool_zalloc(rh->mem, 12))) {
157 		log_error("dm_report_field_uint32: dm_pool_alloc failed");
158 		return 0;
159 	}
160 
161 	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
162 		log_error("dm_report_field_uint32: dm_pool_alloc failed");
163 		return 0;
164 	}
165 
166 	if (dm_snprintf(repstr, 11, "%u", value) < 0) {
167 		log_error("dm_report_field_uint32: uint32 too big: %u", value);
168 		return 0;
169 	}
170 
171 	*sortval = (const uint64_t) value;
172 	field->sort_value = sortval;
173 	field->report_string = repstr;
174 
175 	return 1;
176 }
177 
dm_report_field_int32(struct dm_report * rh,struct dm_report_field * field,const int32_t * data)178 int dm_report_field_int32(struct dm_report *rh,
179 			  struct dm_report_field *field, const int32_t *data)
180 {
181 	const int32_t value = *data;
182 	uint64_t *sortval;
183 	char *repstr;
184 
185 	if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
186 		log_error("dm_report_field_int32: dm_pool_alloc failed");
187 		return 0;
188 	}
189 
190 	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
191 		log_error("dm_report_field_int32: dm_pool_alloc failed");
192 		return 0;
193 	}
194 
195 	if (dm_snprintf(repstr, 12, "%d", value) < 0) {
196 		log_error("dm_report_field_int32: int32 too big: %d", value);
197 		return 0;
198 	}
199 
200 	*sortval = (const uint64_t) value;
201 	field->sort_value = sortval;
202 	field->report_string = repstr;
203 
204 	return 1;
205 }
206 
dm_report_field_uint64(struct dm_report * rh,struct dm_report_field * field,const uint64_t * data)207 int dm_report_field_uint64(struct dm_report *rh,
208 			   struct dm_report_field *field, const uint64_t *data)
209 {
210 	const int value = *data;
211 	uint64_t *sortval;
212 	char *repstr;
213 
214 	if (!(repstr = dm_pool_zalloc(rh->mem, 22))) {
215 		log_error("dm_report_field_uint64: dm_pool_alloc failed");
216 		return 0;
217 	}
218 
219 	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
220 		log_error("dm_report_field_uint64: dm_pool_alloc failed");
221 		return 0;
222 	}
223 
224 	if (dm_snprintf(repstr, 21, "%d", value) < 0) {
225 		log_error("dm_report_field_uint64: uint64 too big: %d", value);
226 		return 0;
227 	}
228 
229 	*sortval = (const uint64_t) value;
230 	field->sort_value = sortval;
231 	field->report_string = repstr;
232 
233 	return 1;
234 }
235 
236 /*
237  * Helper functions for custom report functions
238  */
dm_report_field_set_value(struct dm_report_field * field,const void * value,const void * sortvalue)239 void dm_report_field_set_value(struct dm_report_field *field, const void *value, const void *sortvalue)
240 {
241 	field->report_string = (const char *) value;
242 	field->sort_value = sortvalue ? : value;
243 }
244 
245 /*
246  * show help message
247  */
_display_fields(struct dm_report * rh)248 static void _display_fields(struct dm_report *rh)
249 {
250 	uint32_t f;
251 	const struct dm_report_object_type *type;
252 	const char *desc, *last_desc = "";
253 	size_t id_len = 0;
254 
255 	for (f = 0; rh->fields[f].report_fn; f++)
256 		if (strlen(rh->fields[f].id) > id_len)
257 			id_len = strlen(rh->fields[f].id);
258 
259 
260 	for (type = rh->types; type->data_fn; type++)
261 		if (strlen(type->prefix) + 3 > id_len)
262 			id_len = strlen(type->prefix) + 3;
263 
264 	for (f = 0; rh->fields[f].report_fn; f++) {
265 		if ((type = _find_type(rh, rh->fields[f].type)) && type->desc)
266 			desc = type->desc;
267 		else
268 			desc = " ";
269 		if (desc != last_desc) {
270 			if (*last_desc)
271 				log_warn(" ");
272 			log_warn("%s Fields", desc);
273 			log_warn("%*.*s", (int) strlen(desc) + 7,
274 				 (int) strlen(desc) + 7,
275 				 "-------------------------------------------------------------------------------");
276 			log_warn("  %sall%-*s - %s", type->prefix,
277 				 (int) (id_len - 3 - strlen(type->prefix)), "",
278 				 "All fields in this section.");
279 		}
280 
281 		/* FIXME Add line-wrapping at terminal width (or 80 cols) */
282 		log_warn("  %-*s - %s", (int) id_len, rh->fields[f].id, rh->fields[f].desc);
283 		last_desc = desc;
284 	}
285 }
286 
287 /*
288  * Initialise report handle
289  */
_copy_field(struct dm_report * rh,struct field_properties * dest,uint32_t field_num)290 static int _copy_field(struct dm_report *rh, struct field_properties *dest,
291 		       uint32_t field_num)
292 {
293 	dest->field_num = field_num;
294 	dest->width = rh->fields[field_num].width;
295 	dest->flags = rh->fields[field_num].flags & DM_REPORT_FIELD_MASK;
296 
297 	/* set object type method */
298 	dest->type = _find_type(rh, rh->fields[field_num].type);
299 	if (!dest->type) {
300 		log_error("dm_report: field not match: %s",
301 			  rh->fields[field_num].id);
302 		return 0;
303 	}
304 
305 	return 1;
306 }
307 
_add_field(struct dm_report * rh,uint32_t field_num,uint32_t flags)308 static struct field_properties * _add_field(struct dm_report *rh,
309 					    uint32_t field_num, uint32_t flags)
310 {
311 	struct field_properties *fp;
312 
313 	if (!(fp = dm_pool_zalloc(rh->mem, sizeof(struct field_properties)))) {
314 		log_error("dm_report: struct field_properties allocation "
315 			  "failed");
316 		return NULL;
317 	}
318 
319 	if (!_copy_field(rh, fp, field_num)) {
320 		stack;
321 		dm_pool_free(rh->mem, fp);
322 		return NULL;
323 	}
324 
325 	fp->flags |= flags;
326 
327 	/*
328 	 * Place hidden fields at the front so dm_list_end() will
329 	 * tell us when we've reached the last visible field.
330 	 */
331 	if (fp->flags & FLD_HIDDEN)
332 		dm_list_add_h(&rh->field_props, &fp->list);
333 	else
334 		dm_list_add(&rh->field_props, &fp->list);
335 
336 	return fp;
337 }
338 
339 /*
340  * Compare name1 against name2 or prefix plus name2
341  * name2 is not necessarily null-terminated.
342  * len2 is the length of name2.
343  */
_is_same_field(const char * name1,const char * name2,size_t len2,const char * prefix)344 static int _is_same_field(const char *name1, const char *name2,
345 			  size_t len2, const char *prefix)
346 {
347 	size_t prefix_len;
348 
349 	/* Exact match? */
350 	if (!strncasecmp(name1, name2, len2) && strlen(name1) == len2)
351 		return 1;
352 
353 	/* Match including prefix? */
354 	prefix_len = strlen(prefix);
355 	if (!strncasecmp(prefix, name1, prefix_len) &&
356 	    !strncasecmp(name1 + prefix_len, name2, len2) &&
357 	    strlen(name1) == prefix_len + len2)
358 		return 1;
359 
360 	return 0;
361 }
362 
363 /*
364  * Check for a report type prefix + "all" match.
365  */
_all_match(struct dm_report * rh,const char * field,size_t flen)366 static uint32_t _all_match(struct dm_report *rh, const char *field, size_t flen)
367 {
368 	size_t prefix_len;
369 	const struct dm_report_object_type *t;
370 	char prefixed_all[32];
371 
372 	if (!strncasecmp(field, "all", 3) && flen == 3) {
373 		if (strlen(rh->field_prefix)) {
374 			strcpy(prefixed_all, rh->field_prefix);
375 			strcat(prefixed_all, "all");
376 			/*
377 			 * Add also prefix to receive all attributes
378 			 * (e.g.LABEL/PVS use the same prefix)
379 			 */
380 			return rh->report_types |
381 			       _all_match(rh, prefixed_all,
382 					  strlen(prefixed_all));
383 		} else
384 			return rh->report_types;
385 	}
386 
387 	for (t = rh->types; t->data_fn; t++) {
388 		prefix_len = strlen(t->prefix);
389 		if (!strncasecmp(t->prefix, field, prefix_len) &&
390 		    !strncasecmp(field + prefix_len, "all", 3) &&
391 		    flen == prefix_len + 3)
392 			return t->id;
393 	}
394 
395 	return 0;
396 }
397 
398 /*
399  * Add all fields with a matching type.
400  */
_add_all_fields(struct dm_report * rh,uint32_t type)401 static int _add_all_fields(struct dm_report *rh, uint32_t type)
402 {
403 	uint32_t f;
404 
405 	for (f = 0; rh->fields[f].report_fn; f++)
406 		if ((rh->fields[f].type & type) && !_add_field(rh, f, 0))
407 			return 0;
408 
409 	return 1;
410 }
411 
_field_match(struct dm_report * rh,const char * field,size_t flen,unsigned report_type_only)412 static int _field_match(struct dm_report *rh, const char *field, size_t flen,
413 			unsigned report_type_only)
414 {
415 	uint32_t f, type;
416 
417 	if (!flen)
418 		return 0;
419 
420 	for (f = 0; rh->fields[f].report_fn; f++)
421 		if (_is_same_field(rh->fields[f].id, field, flen,
422 				   rh->field_prefix)) {
423 			if (report_type_only) {
424 				rh->report_types |= rh->fields[f].type;
425 				return 1;
426 			} else
427 				return _add_field(rh, f, 0) ? 1 : 0;
428 		}
429 
430 	if ((type = _all_match(rh, field, flen))) {
431 		if (report_type_only) {
432 			rh->report_types |= type;
433 			return 1;
434 		} else
435 			return  _add_all_fields(rh, type);
436 	}
437 
438 	return 0;
439 }
440 
_add_sort_key(struct dm_report * rh,uint32_t field_num,uint32_t flags,unsigned report_type_only)441 static int _add_sort_key(struct dm_report *rh, uint32_t field_num,
442 			 uint32_t flags, unsigned report_type_only)
443 {
444 	struct field_properties *fp, *found = NULL;
445 
446 	dm_list_iterate_items(fp, &rh->field_props) {
447 		if (fp->field_num == field_num) {
448 			found = fp;
449 			break;
450 		}
451 	}
452 
453 	if (!found) {
454 		if (report_type_only)
455 			rh->report_types |= rh->fields[field_num].type;
456 		else if (!(found = _add_field(rh, field_num, FLD_HIDDEN)))
457 			return_0;
458 	}
459 
460 	if (report_type_only)
461 		return 1;
462 
463 	if (found->flags & FLD_SORT_KEY) {
464 		log_error("dm_report: Ignoring duplicate sort field: %s",
465 			  rh->fields[field_num].id);
466 		return 1;
467 	}
468 
469 	found->flags |= FLD_SORT_KEY;
470 	found->sort_posn = rh->keys_count++;
471 	found->flags |= flags;
472 
473 	return 1;
474 }
475 
_key_match(struct dm_report * rh,const char * key,size_t len,unsigned report_type_only)476 static int _key_match(struct dm_report *rh, const char *key, size_t len,
477 		      unsigned report_type_only)
478 {
479 	uint32_t f;
480 	uint32_t flags;
481 
482 	if (!len)
483 		return 0;
484 
485 	if (*key == '+') {
486 		key++;
487 		len--;
488 		flags = FLD_ASCENDING;
489 	} else if (*key == '-') {
490 		key++;
491 		len--;
492 		flags = FLD_DESCENDING;
493 	} else
494 		flags = FLD_ASCENDING;
495 
496 	if (!len) {
497 		log_error("dm_report: Missing sort field name");
498 		return 0;
499 	}
500 
501 	for (f = 0; rh->fields[f].report_fn; f++)
502 		if (_is_same_field(rh->fields[f].id, key, len,
503 				   rh->field_prefix))
504 			return _add_sort_key(rh, f, flags, report_type_only);
505 
506 	return 0;
507 }
508 
_parse_fields(struct dm_report * rh,const char * format,unsigned report_type_only)509 static int _parse_fields(struct dm_report *rh, const char *format,
510 			 unsigned report_type_only)
511 {
512 	const char *ws;		/* Word start */
513 	const char *we = format;	/* Word end */
514 
515 	while (*we) {
516 		/* Allow consecutive commas */
517 		while (*we && *we == ',')
518 			we++;
519 
520 		/* start of the field name */
521 		ws = we;
522 		while (*we && *we != ',')
523 			we++;
524 
525 		if (!_field_match(rh, ws, (size_t) (we - ws), report_type_only)) {
526 			_display_fields(rh);
527 			log_warn(" ");
528 			if (strcasecmp(ws, "help") && strcmp(ws, "?"))
529 				log_error("Unrecognised field: %.*s",
530 					  (int) (we - ws), ws);
531 			return 0;
532 		}
533 	}
534 
535 	return 1;
536 }
537 
_parse_keys(struct dm_report * rh,const char * keys,unsigned report_type_only)538 static int _parse_keys(struct dm_report *rh, const char *keys,
539 		       unsigned report_type_only)
540 {
541 	const char *ws;		/* Word start */
542 	const char *we = keys;	/* Word end */
543 
544 	while (*we) {
545 		/* Allow consecutive commas */
546 		while (*we && *we == ',')
547 			we++;
548 		ws = we;
549 		while (*we && *we != ',')
550 			we++;
551 		if (!_key_match(rh, ws, (size_t) (we - ws), report_type_only)) {
552 			log_error("dm_report: Unrecognised field: %.*s",
553 				  (int) (we - ws), ws);
554 			return 0;
555 		}
556 	}
557 
558 	return 1;
559 }
560 
dm_report_init(uint32_t * report_types,const struct dm_report_object_type * types,const struct dm_report_field_type * fields,const char * output_fields,const char * output_separator,uint32_t output_flags,const char * sort_keys,void * private)561 struct dm_report *dm_report_init(uint32_t *report_types,
562 				 const struct dm_report_object_type *types,
563 				 const struct dm_report_field_type *fields,
564 				 const char *output_fields,
565 				 const char *output_separator,
566 				 uint32_t output_flags,
567 				 const char *sort_keys,
568 				 void *private)
569 {
570 	struct dm_report *rh;
571 	const struct dm_report_object_type *type;
572 
573 	if (!(rh = dm_malloc(sizeof(*rh)))) {
574 		log_error("dm_report_init: dm_malloc failed");
575 		return 0;
576 	}
577 	memset(rh, 0, sizeof(*rh));
578 
579 	/*
580 	 * rh->report_types is updated in _parse_fields() and _parse_keys()
581 	 * to contain all types corresponding to the fields specified by
582 	 * fields or keys.
583 	 */
584 	if (report_types)
585 		rh->report_types = *report_types;
586 
587 	rh->separator = output_separator;
588 	rh->fields = fields;
589 	rh->types = types;
590 	rh->private = private;
591 
592 	rh->flags |= output_flags & DM_REPORT_OUTPUT_MASK;
593 
594 	/* With columns_as_rows we must buffer and not align. */
595 	if (output_flags & DM_REPORT_OUTPUT_COLUMNS_AS_ROWS) {
596 		if (!(output_flags & DM_REPORT_OUTPUT_BUFFERED))
597 			rh->flags |= DM_REPORT_OUTPUT_BUFFERED;
598 		if (output_flags & DM_REPORT_OUTPUT_ALIGNED)
599 			rh->flags &= ~DM_REPORT_OUTPUT_ALIGNED;
600 	}
601 
602 	if (output_flags & DM_REPORT_OUTPUT_BUFFERED)
603 		rh->flags |= RH_SORT_REQUIRED;
604 
605 	dm_list_init(&rh->field_props);
606 	dm_list_init(&rh->rows);
607 
608 	if ((type = _find_type(rh, rh->report_types)) && type->prefix)
609 		rh->field_prefix = type->prefix;
610 	else
611 		rh->field_prefix = "";
612 
613 	if (!(rh->mem = dm_pool_create("report", 10 * 1024))) {
614 		log_error("dm_report_init: allocation of memory pool failed");
615 		dm_free(rh);
616 		return NULL;
617 	}
618 
619 	/*
620 	 * To keep the code needed to add the "all" field to a minimum, we parse
621 	 * the field lists twice.  The first time we only update the report type.
622 	 * FIXME Use one pass instead and expand the "all" field afterwards.
623 	 */
624 	if (!_parse_fields(rh, output_fields, 1) ||
625 	    !_parse_keys(rh, sort_keys, 1)) {
626 		dm_report_free(rh);
627 		return NULL;
628 	}
629 
630 	/* Generate list of fields for output based on format string & flags */
631 	if (!_parse_fields(rh, output_fields, 0) ||
632 	    !_parse_keys(rh, sort_keys, 0)) {
633 		dm_report_free(rh);
634 		return NULL;
635 	}
636 
637 	/* Return updated types value for further compatility check by caller */
638 	if (report_types)
639 		*report_types = rh->report_types;
640 
641 	return rh;
642 }
643 
dm_report_free(struct dm_report * rh)644 void dm_report_free(struct dm_report *rh)
645 {
646 	dm_pool_destroy(rh->mem);
647 	dm_free(rh);
648 }
649 
_toupperstr(char * str)650 static char *_toupperstr(char *str)
651 {
652 	char *u = str;
653 
654 	do
655 		*u = toupper(*u);
656 	while (*u++);
657 
658 	return str;
659 }
660 
dm_report_set_output_field_name_prefix(struct dm_report * rh,const char * output_field_name_prefix)661 int dm_report_set_output_field_name_prefix(struct dm_report *rh, const char *output_field_name_prefix)
662 {
663 	char *prefix;
664 
665 	if (!(prefix = dm_pool_strdup(rh->mem, output_field_name_prefix))) {
666 		log_error("dm_report_set_output_field_name_prefix: dm_pool_strdup failed");
667 		return 0;
668 	}
669 
670 	rh->output_field_name_prefix = _toupperstr(prefix);
671 
672 	return 1;
673 }
674 
675 /*
676  * Create a row of data for an object
677  */
_report_get_field_data(struct dm_report * rh,struct field_properties * fp,void * object)678 static void * _report_get_field_data(struct dm_report *rh,
679 			      struct field_properties *fp, void *object)
680 {
681 	void *ret = fp->type->data_fn(object);
682 
683 	if (!ret)
684 		return NULL;
685 
686 	return ret + rh->fields[fp->field_num].offset;
687 }
688 
dm_report_object(struct dm_report * rh,void * object)689 int dm_report_object(struct dm_report *rh, void *object)
690 {
691 	struct field_properties *fp;
692 	struct row *row;
693 	struct dm_report_field *field;
694 	void *data = NULL;
695 
696 	if (!(row = dm_pool_zalloc(rh->mem, sizeof(*row)))) {
697 		log_error("dm_report_object: struct row allocation failed");
698 		return 0;
699 	}
700 
701 	row->rh = rh;
702 
703 	if ((rh->flags & RH_SORT_REQUIRED) &&
704 	    !(row->sort_fields =
705 		dm_pool_zalloc(rh->mem, sizeof(struct dm_report_field *) *
706 			       rh->keys_count))) {
707 		log_error("dm_report_object: "
708 			  "row sort value structure allocation failed");
709 		return 0;
710 	}
711 
712 	dm_list_init(&row->fields);
713 	dm_list_add(&rh->rows, &row->list);
714 
715 	/* For each field to be displayed, call its report_fn */
716 	dm_list_iterate_items(fp, &rh->field_props) {
717 		if (!(field = dm_pool_zalloc(rh->mem, sizeof(*field)))) {
718 			log_error("dm_report_object: "
719 				  "struct dm_report_field allocation failed");
720 			return 0;
721 		}
722 		field->props = fp;
723 
724 		data = _report_get_field_data(rh, fp, object);
725 		if (!data)
726 			return 0;
727 
728 		if (!rh->fields[fp->field_num].report_fn(rh, rh->mem,
729 							 field, data,
730 							 rh->private)) {
731 			log_error("dm_report_object: "
732 				  "report function failed for field %s",
733 				  rh->fields[fp->field_num].id);
734 			return 0;
735 		}
736 
737 		if ((strlen(field->report_string) > field->props->width))
738 			field->props->width = strlen(field->report_string);
739 
740 		if ((rh->flags & RH_SORT_REQUIRED) &&
741 		    (field->props->flags & FLD_SORT_KEY)) {
742 			(*row->sort_fields)[field->props->sort_posn] = field;
743 		}
744 		dm_list_add(&row->fields, &field->list);
745 	}
746 
747 	if (!(rh->flags & DM_REPORT_OUTPUT_BUFFERED))
748 		return dm_report_output(rh);
749 
750 	return 1;
751 }
752 
753 /*
754  * Print row of headings
755  */
_report_headings(struct dm_report * rh)756 static int _report_headings(struct dm_report *rh)
757 {
758 	struct field_properties *fp;
759 	const char *heading;
760 	char buf[1024];
761 
762 	if (rh->flags & RH_HEADINGS_PRINTED)
763 		return 1;
764 
765 	rh->flags |= RH_HEADINGS_PRINTED;
766 
767 	if (!(rh->flags & DM_REPORT_OUTPUT_HEADINGS))
768 		return 1;
769 
770 	if (!dm_pool_begin_object(rh->mem, 128)) {
771 		log_error("dm_report: "
772 			  "dm_pool_begin_object failed for headings");
773 		return 0;
774 	}
775 
776 	/* First heading line */
777 	dm_list_iterate_items(fp, &rh->field_props) {
778 		if (fp->flags & FLD_HIDDEN)
779 			continue;
780 
781 		heading = rh->fields[fp->field_num].heading;
782 		if (rh->flags & DM_REPORT_OUTPUT_ALIGNED) {
783 			if (dm_snprintf(buf, sizeof(buf), "%-*.*s",
784 					 fp->width, fp->width, heading) < 0) {
785 				log_error("dm_report: snprintf heading failed");
786 				goto bad;
787 			}
788 			if (!dm_pool_grow_object(rh->mem, buf, fp->width)) {
789 				log_error("dm_report: Failed to generate report headings for printing");
790 				goto bad;
791 			}
792 		} else if (!dm_pool_grow_object(rh->mem, heading, 0)) {
793 			log_error("dm_report: Failed to generate report headings for printing");
794 			goto bad;
795 		}
796 
797 		if (!dm_list_end(&rh->field_props, &fp->list))
798 			if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
799 				log_error("dm_report: Failed to generate report headings for printing");
800 				goto bad;
801 			}
802 	}
803 	if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
804 		log_error("dm_report: Failed to generate report headings for printing");
805 		goto bad;
806 	}
807 	log_print("%s", (char *) dm_pool_end_object(rh->mem));
808 
809 	return 1;
810 
811       bad:
812 	dm_pool_abandon_object(rh->mem);
813 	return 0;
814 }
815 
816 /*
817  * Sort rows of data
818  */
_row_compare(const void * a,const void * b)819 static int _row_compare(const void *a, const void *b)
820 {
821 	const struct row *rowa = *(const struct row **) a;
822 	const struct row *rowb = *(const struct row **) b;
823 	const struct dm_report_field *sfa, *sfb;
824 	uint32_t cnt;
825 
826 	for (cnt = 0; cnt < rowa->rh->keys_count; cnt++) {
827 		sfa = (*rowa->sort_fields)[cnt];
828 		sfb = (*rowb->sort_fields)[cnt];
829 		if (sfa->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) {
830 			const uint64_t numa =
831 			    *(const uint64_t *) sfa->sort_value;
832 			const uint64_t numb =
833 			    *(const uint64_t *) sfb->sort_value;
834 
835 			if (numa == numb)
836 				continue;
837 
838 			if (sfa->props->flags & FLD_ASCENDING) {
839 				return (numa > numb) ? 1 : -1;
840 			} else {	/* FLD_DESCENDING */
841 				return (numa < numb) ? 1 : -1;
842 			}
843 		} else {	/* DM_REPORT_FIELD_TYPE_STRING */
844 			const char *stra = (const char *) sfa->sort_value;
845 			const char *strb = (const char *) sfb->sort_value;
846 			int cmp = strcmp(stra, strb);
847 
848 			if (!cmp)
849 				continue;
850 
851 			if (sfa->props->flags & FLD_ASCENDING) {
852 				return (cmp > 0) ? 1 : -1;
853 			} else {	/* FLD_DESCENDING */
854 				return (cmp < 0) ? 1 : -1;
855 			}
856 		}
857 	}
858 
859 	return 0;		/* Identical */
860 }
861 
_sort_rows(struct dm_report * rh)862 static int _sort_rows(struct dm_report *rh)
863 {
864 	struct row *(*rows)[];
865 	uint32_t count = 0;
866 	struct row *row;
867 
868 	if (!(rows = dm_pool_alloc(rh->mem, sizeof(**rows) *
869 				dm_list_size(&rh->rows)))) {
870 		log_error("dm_report: sort array allocation failed");
871 		return 0;
872 	}
873 
874 	dm_list_iterate_items(row, &rh->rows)
875 		(*rows)[count++] = row;
876 
877 	qsort(rows, count, sizeof(**rows), _row_compare);
878 
879 	dm_list_init(&rh->rows);
880 	while (count--)
881 		dm_list_add_h(&rh->rows, &(*rows)[count]->list);
882 
883 	return 1;
884 }
885 
886 /*
887  * Produce report output
888  */
_output_field(struct dm_report * rh,struct dm_report_field * field)889 static int _output_field(struct dm_report *rh, struct dm_report_field *field)
890 {
891 	char *field_id;
892 	int32_t width;
893 	uint32_t align;
894 	const char *repstr;
895 	char buf[4096];
896 
897 	if (rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) {
898 		if (!(field_id = strdup(rh->fields[field->props->field_num].id))) {
899 			log_error("dm_report: Failed to copy field name");
900 			return 0;
901 		}
902 
903 		if (!dm_pool_grow_object(rh->mem, rh->output_field_name_prefix, 0)) {
904 			log_error("dm_report: Unable to extend output line");
905 			return 0;
906 		}
907 
908 		if (!dm_pool_grow_object(rh->mem, _toupperstr(field_id), 0)) {
909 			log_error("dm_report: Unable to extend output line");
910 			return 0;
911 		}
912 
913 		free(field_id);
914 
915 		if (!dm_pool_grow_object(rh->mem, "=", 1)) {
916 			log_error("dm_report: Unable to extend output line");
917 			return 0;
918 		}
919 
920 		if (!(rh->flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED) &&
921 		    !dm_pool_grow_object(rh->mem, "\'", 1)) {
922 			log_error("dm_report: Unable to extend output line");
923 			return 0;
924 		}
925 	}
926 
927 	repstr = field->report_string;
928 	width = field->props->width;
929 	if (!(rh->flags & DM_REPORT_OUTPUT_ALIGNED)) {
930 		if (!dm_pool_grow_object(rh->mem, repstr, 0)) {
931 			log_error("dm_report: Unable to extend output line");
932 			return 0;
933 		}
934 	} else {
935 		if (!(align = field->props->flags & DM_REPORT_FIELD_ALIGN_MASK))
936 			align = (field->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) ?
937 				DM_REPORT_FIELD_ALIGN_RIGHT : DM_REPORT_FIELD_ALIGN_LEFT;
938 		if (align & DM_REPORT_FIELD_ALIGN_LEFT) {
939 			if (dm_snprintf(buf, sizeof(buf), "%-*.*s",
940 					 width, width, repstr) < 0) {
941 				log_error("dm_report: left-aligned snprintf() failed");
942 				return 0;
943 			}
944 			if (!dm_pool_grow_object(rh->mem, buf, width)) {
945 				log_error("dm_report: Unable to extend output line");
946 				return 0;
947 			}
948 		} else if (align & DM_REPORT_FIELD_ALIGN_RIGHT) {
949 			if (dm_snprintf(buf, sizeof(buf), "%*.*s",
950 					 width, width, repstr) < 0) {
951 				log_error("dm_report: right-aligned snprintf() failed");
952 				return 0;
953 			}
954 			if (!dm_pool_grow_object(rh->mem, buf, width)) {
955 				log_error("dm_report: Unable to extend output line");
956 				return 0;
957 			}
958 		}
959 	}
960 
961 	if ((rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) &&
962 	    !(rh->flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED))
963 		if (!dm_pool_grow_object(rh->mem, "\'", 1)) {
964 			log_error("dm_report: Unable to extend output line");
965 			return 0;
966 		}
967 
968 	return 1;
969 }
970 
_output_as_rows(struct dm_report * rh)971 static int _output_as_rows(struct dm_report *rh)
972 {
973 	struct field_properties *fp;
974 	struct dm_report_field *field;
975 	struct row *row;
976 
977 	if (!dm_pool_begin_object(rh->mem, 512)) {
978 		log_error("dm_report: Unable to allocate output line");
979 		return 0;
980 	}
981 
982 	dm_list_iterate_items(fp, &rh->field_props) {
983 		if (fp->flags & FLD_HIDDEN) {
984 			dm_list_iterate_items(row, &rh->rows) {
985 				field = dm_list_item(dm_list_first(&row->fields), struct dm_report_field);
986 				dm_list_del(&field->list);
987 			}
988 			continue;
989 		}
990 
991 		if ((rh->flags & DM_REPORT_OUTPUT_HEADINGS)) {
992 			if (!dm_pool_grow_object(rh->mem, rh->fields[fp->field_num].heading, 0)) {
993 				log_error("dm_report: Failed to extend row for field name");
994 				goto bad;
995 			}
996 			if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
997 				log_error("dm_report: Failed to extend row with separator");
998 				goto bad;
999 			}
1000 		}
1001 
1002 		dm_list_iterate_items(row, &rh->rows) {
1003 			if ((field = dm_list_item(dm_list_first(&row->fields), struct dm_report_field))) {
1004 				if (!_output_field(rh, field))
1005 					goto bad;
1006 				dm_list_del(&field->list);
1007 			}
1008 
1009 			if (!dm_list_end(&rh->rows, &row->list))
1010 				if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
1011 					log_error("dm_report: Unable to extend output line");
1012 					goto bad;
1013 				}
1014 		}
1015 
1016 		if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
1017 			log_error("dm_report: Failed to terminate row");
1018 			goto bad;
1019 		}
1020 		log_print("%s", (char *) dm_pool_end_object(rh->mem));
1021 	}
1022 
1023 	return 1;
1024 
1025       bad:
1026 	dm_pool_abandon_object(rh->mem);
1027 	return 0;
1028 }
1029 
_output_as_columns(struct dm_report * rh)1030 static int _output_as_columns(struct dm_report *rh)
1031 {
1032 	struct dm_list *fh, *rowh, *ftmp, *rtmp;
1033 	struct row *row = NULL;
1034 	struct dm_report_field *field;
1035 
1036 	/* If headings not printed yet, calculate field widths and print them */
1037 	if (!(rh->flags & RH_HEADINGS_PRINTED))
1038 		_report_headings(rh);
1039 
1040 	/* Print and clear buffer */
1041 	dm_list_iterate_safe(rowh, rtmp, &rh->rows) {
1042 		if (!dm_pool_begin_object(rh->mem, 512)) {
1043 			log_error("dm_report: Unable to allocate output line");
1044 			return 0;
1045 		}
1046 		row = dm_list_item(rowh, struct row);
1047 		dm_list_iterate_safe(fh, ftmp, &row->fields) {
1048 			field = dm_list_item(fh, struct dm_report_field);
1049 			if (field->props->flags & FLD_HIDDEN)
1050 				continue;
1051 
1052 			if (!_output_field(rh, field))
1053 				goto bad;
1054 
1055 			if (!dm_list_end(&row->fields, fh))
1056 				if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
1057 					log_error("dm_report: Unable to extend output line");
1058 					goto bad;
1059 				}
1060 
1061 			dm_list_del(&field->list);
1062 		}
1063 		if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
1064 			log_error("dm_report: Unable to terminate output line");
1065 			goto bad;
1066 		}
1067 		log_print("%s", (char *) dm_pool_end_object(rh->mem));
1068 		dm_list_del(&row->list);
1069 	}
1070 
1071 	if (row)
1072 		dm_pool_free(rh->mem, row);
1073 
1074 	return 1;
1075 
1076       bad:
1077 	dm_pool_abandon_object(rh->mem);
1078 	return 0;
1079 }
1080 
dm_report_output(struct dm_report * rh)1081 int dm_report_output(struct dm_report *rh)
1082 {
1083 	if (dm_list_empty(&rh->rows))
1084 		return 1;
1085 
1086 	if ((rh->flags & RH_SORT_REQUIRED))
1087 		_sort_rows(rh);
1088 
1089 	if ((rh->flags & DM_REPORT_OUTPUT_COLUMNS_AS_ROWS))
1090 		return _output_as_rows(rh);
1091 	else
1092 		return _output_as_columns(rh);
1093 }
1094