1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <stdarg.h>
29#include <string.h>
30#include <alloca.h>
31#include <libintl.h>
32#include <locale.h>
33#include <unistd.h>
34#include <assert.h>
35#include <inttypes.h>
36#include <sys/termios.h>
37#include <picl.h>
38
39/*
40 * Constant definitions and macros
41 */
42#define	COL_DELIM		"|"
43#define	ROOT_LEVEL		0
44#define	LEVEL_INDENT		4
45#define	PROP_INDENT		2
46#define	NCOLS			80
47#define	NODEINFO_LEFT_MARGIN(x)	(x * LEVEL_INDENT)
48#define	PROPINFO_LEFT_MARGIN(x)	(x * LEVEL_INDENT + PROP_INDENT)
49
50#define	PRIxPICLTBL		PRIx64
51#define	PRIxPICLHDL		PRIx64
52
53/*
54 * Program variables
55 */
56static	char	*prog;
57static	int	verbose_mode = 0;
58
59/*
60 * Error codes
61 */
62#define	EM_USAGE		0
63#define	EM_INIT			1
64#define	EM_GETROOT		2
65#define	EM_GETPVAL		3
66#define	EM_GETNXTBYCOL		4
67#define	EM_GETNXTBYROW		5
68#define	EM_GETPINFO		6
69#define	EM_GETPVALBYNAME	7
70#define	EM_GETPROPBYNAME	8
71#define	EM_INT_INVSIZE		9
72#define	EM_UINT_INVSIZE		10
73#define	EM_FLOAT_INVSIZE	11
74#define	EM_TS_INVALID		12
75#define	EM_TABLE_INVSIZE	13
76#define	EM_REF_INVSIZE		14
77#define	EM_TYPE_UNKNOWN		15
78#define	EM_TS_OVERFLOW		16
79#define	EM_TS_INVSIZE		17
80
81/*
82 * Error mesage texts
83 */
84static	char	*err_msg[] = {
85	/* program usage */
86	"Usage: %s [-v] [-c <picl_class>]\n",			/*  0 */
87	/* picl call failed messages */
88	"picl_initialize failed: %s\n",				/*  1 */
89	"picl_get_root failed: %s\n",				/*  2 */
90	"picl_get_propval failed: %s\n",			/*  3 */
91	"picl_get_next_by_col failed: %s\n",			/*  4 */
92	"picl_get_next_by_row failed: %s\n",			/*  5 */
93	"picl_get_propinfo failed: %s\n",			/*  6 */
94	"picl_get_propval_by_name failed: %s\n",		/*  7 */
95	"picl_get_prop_by_name failed: %s\n",			/*  8 */
96	/* invalid data error messages */
97	"picl_get_propval: invalid int size %d\n",		/*  9 */
98	"picl_get_propval: invalid unsigned int size %d\n",	/* 10 */
99	"picl_get_propval: invalid float size %d\n",		/* 11 */
100	"picl_get_propval: invalid timestamp\n",		/* 12 */
101	"picl_get_propval: invalid table handle size %d\n",	/* 13 */
102	"picl_get_propval: invalid reference size %d\n",	/* 14 */
103	"picl_get_propval: unknown type\n",			/* 15 */
104	"picl_get_propval: timestamp value too large\n",	/* 16 */
105	"picl_get_propval: invalid timestamp size\n"		/* 17 */
106};
107
108/*PRINTFLIKE1*/
109static void
110print_errmsg(char *message, ...)
111{
112	va_list ap;
113
114	va_start(ap, message);
115	(void) fprintf(stderr, "%s: ", prog);
116	(void) vfprintf(stderr, message, ap);
117	va_end(ap);
118}
119
120/*
121 * Print prtpicl usage
122 */
123static void
124usage(void)
125{
126	print_errmsg(gettext(err_msg[EM_USAGE]), prog);
127	exit(1);
128}
129
130/*
131 * print a bytearray value and format it to fit in 80 columns
132 */
133static void
134print_bytearray(int lvl, uint8_t *vbuf, size_t nbytes)
135{
136	int		cnum;
137	int		columns;
138	char		*s;
139	struct winsize	winsize;
140	size_t		i;
141
142	/*
143	 * The COLUMNS_PER_BYTE is set to 4 to match the printf
144	 * format used below, i.e. " %02x ", to print a byte
145	 */
146#define	COLUMNS_PER_BYTE	4
147
148	/*
149	 * Kind of a hack to determine the width of the output...
150	 */
151	columns = NCOLS;
152	if ((s = getenv("COLUMNS")) != NULL && (cnum = atoi(s)) > 0)
153		columns = cnum;
154	else if (isatty(fileno(stdout)) &&
155	    ioctl(fileno(stdout), TIOCGWINSZ, &winsize) == 0 &&
156	    winsize.ws_col != 0)
157		columns = winsize.ws_col;
158
159
160	cnum = PROPINFO_LEFT_MARGIN(lvl);
161	if ((nbytes * COLUMNS_PER_BYTE + cnum) > columns) {
162		(void) printf("\n");
163		cnum = 0;
164	}
165	for (i = 0; i < nbytes; ++i) {
166		if (cnum > columns - COLUMNS_PER_BYTE) {
167			(void) printf("\n");
168			cnum = 0;
169		}
170		(void) printf(" %02x ", vbuf[i]);
171		cnum += COLUMNS_PER_BYTE;
172	}
173}
174
175/*
176 * Print a property's value
177 * If the property is read protected, return success.
178 * If an invalid/stale handle error is encountered, return the error. For
179 * other errors, print a message and return success.
180 */
181static int
182print_propval(int lvl, picl_prophdl_t proph, const picl_propinfo_t *propinfo)
183{
184	int		err;
185	void		*vbuf;
186	char		*str;
187	uint64_t	val64;
188	time_t		tmp;
189
190	/*
191	 * If property is read protected, print a message and continue
192	 */
193	if (!(propinfo->accessmode & PICL_READ)) {
194		(void) printf("<%s>", gettext("WRITE-ONLY"));
195		return (PICL_SUCCESS);
196	}
197
198	vbuf = alloca(propinfo->size);
199	if (propinfo->type == PICL_PTYPE_VOID)
200		return (PICL_SUCCESS);
201
202	err = picl_get_propval(proph, vbuf, propinfo->size);
203	/*
204	 * If the error is not a stale/invalid handle or noresponse, continue
205	 * by ignoring the error/skipping the property.
206	 */
207	if ((err == PICL_INVALIDHANDLE) || (err == PICL_STALEHANDLE) ||
208	    (err == PICL_NORESPONSE))
209		return (err);
210	else if (err != PICL_SUCCESS) {
211		(void) printf("<%s: %s>", gettext("ERROR"), picl_strerror(err));
212		return (PICL_SUCCESS);
213	}
214
215	switch (propinfo->type) {
216	case PICL_PTYPE_CHARSTRING:
217		if (propinfo->size > 0)
218			(void) printf(" %s ", (char *)vbuf);
219		break;
220	case PICL_PTYPE_INT:
221		switch (propinfo->size) {
222		case sizeof (int8_t):
223			/* avoid using PRId8 until lint recognizes hh */
224			(void) printf(" %d ", *(int8_t *)vbuf);
225			break;
226		case sizeof (int16_t):
227			(void) printf(" %" PRId16 " ", *(int16_t *)vbuf);
228			break;
229		case sizeof (int32_t):
230			(void) printf(" %" PRId32 " ", *(int32_t *)vbuf);
231			break;
232		case sizeof (int64_t):
233			(void) printf(" %" PRId64 " ", *(int64_t *)vbuf);
234			break;
235		default:
236			print_errmsg(gettext(err_msg[EM_INT_INVSIZE]),
237			    propinfo->size);
238			return (PICL_FAILURE);
239		}
240		break;
241	case PICL_PTYPE_UNSIGNED_INT:
242		switch (propinfo->size) {
243		case sizeof (uint8_t):
244			/* avoid using PRIx8 until lint recognizes hh */
245			(void) printf(" %#x ", *(uint8_t *)vbuf);
246			break;
247		case sizeof (uint16_t):
248			(void) printf(" %#" PRIx16 " ", *(uint16_t *)vbuf);
249			break;
250		case sizeof (uint32_t):
251			(void) printf(" %#" PRIx32 " ", *(uint32_t *)vbuf);
252			break;
253		case sizeof (uint64_t):
254			(void) printf(" %#" PRIx64 " ", *(uint64_t *)vbuf);
255			break;
256		default:
257			print_errmsg(gettext(err_msg[EM_UINT_INVSIZE]),
258			    propinfo->size);
259			return (PICL_FAILURE);
260		}
261		break;
262	case PICL_PTYPE_FLOAT:
263		switch (propinfo->size) {
264		case sizeof (float):
265			(void) printf(" %f ", *(float *)vbuf);
266			break;
267		case sizeof (double):
268			(void) printf(" %f ", *(double *)vbuf);
269			break;
270		default:
271			print_errmsg(gettext(err_msg[EM_FLOAT_INVSIZE]),
272			    propinfo->size);
273			return (PICL_FAILURE);
274		}
275		break;
276	case PICL_PTYPE_TIMESTAMP:
277		if (propinfo->size != sizeof (val64)) {
278			print_errmsg(gettext(err_msg[EM_TS_INVSIZE]));
279			return (PICL_FAILURE);
280		}
281		val64 = *(uint64_t *)vbuf;
282		tmp = (time_t)val64;
283		if ((uint64_t)tmp != val64) {
284			print_errmsg(gettext(err_msg[EM_TS_OVERFLOW]));
285			return (PICL_FAILURE);
286		}
287		str = ctime(&tmp);
288		if (str == NULL) {
289			print_errmsg(gettext(err_msg[EM_TS_INVALID]));
290			return (PICL_FAILURE);
291		}
292		str[strlen(str) - 1] = '\0';
293		(void) printf(" %s ", str);
294		break;
295	case PICL_PTYPE_TABLE:
296		if (propinfo->size != sizeof (picl_prophdl_t)) {
297			print_errmsg(gettext(err_msg[EM_TABLE_INVSIZE]),
298			    propinfo->size);
299			return (PICL_FAILURE);
300		}
301		(void) printf("(%" PRIxPICLTBL "TBL) ",
302		    *(picl_prophdl_t *)vbuf);
303		break;
304	case PICL_PTYPE_REFERENCE:
305		if (propinfo->size != sizeof (picl_nodehdl_t)) {
306			print_errmsg(gettext(err_msg[EM_REF_INVSIZE]),
307			    propinfo->size);
308			return (PICL_FAILURE);
309		}
310		(void) printf(" (%" PRIxPICLHDL "H) ", *(picl_nodehdl_t *)vbuf);
311		break;
312	case PICL_PTYPE_BYTEARRAY:
313		if (propinfo->size > 0)
314			print_bytearray(lvl, vbuf, propinfo->size);
315		break;
316	default:
317		print_errmsg(gettext(err_msg[EM_TYPE_UNKNOWN]));
318		return (PICL_FAILURE);
319	}
320	return (PICL_SUCCESS);
321}
322
323/*
324 * print table property value
325 */
326static int
327print_table_prop(int lvl, picl_prophdl_t tblh)
328{
329	picl_prophdl_t	rowproph;
330	picl_prophdl_t	colproph;
331	int		err;
332	picl_propinfo_t	propinfo;
333
334	for (err = picl_get_next_by_col(tblh, &rowproph); err != PICL_ENDOFLIST;
335	    err = picl_get_next_by_col(rowproph, &rowproph)) {
336		if (err != PICL_SUCCESS) {
337			print_errmsg(gettext(err_msg[EM_GETNXTBYCOL]),
338			    picl_strerror(err));
339			return (err);
340		}
341
342		(void) printf("%*s %s", PROPINFO_LEFT_MARGIN(lvl), " ",
343		    COL_DELIM);
344
345		for (colproph = rowproph; err != PICL_ENDOFLIST;
346		    err = picl_get_next_by_row(colproph, &colproph)) {
347
348			if (err != PICL_SUCCESS) {
349				print_errmsg(gettext(err_msg[EM_GETNXTBYROW]),
350				    picl_strerror(err));
351				return (err);
352			}
353
354			err = picl_get_propinfo(colproph, &propinfo);
355			if (err != PICL_SUCCESS) {
356				print_errmsg(gettext(err_msg[EM_GETPINFO]),
357				    picl_strerror(err));
358				return (err);
359			}
360
361			err = print_propval(lvl, colproph, &propinfo);
362			if (err != PICL_SUCCESS)
363				return (err);
364			(void) printf(COL_DELIM);
365		}
366		(void) printf("\n");
367	}
368	return (PICL_SUCCESS);
369}
370
371/*
372 * Print the properties (name = value) of a node. If an error occurs
373 * when printing the property value, stop. print_propval() suppresses
374 * errors during getting property value except for stale/invalid handle
375 * and no response errors.
376 */
377static int
378print_proplist(int lvl, picl_nodehdl_t nodeh)
379{
380	int		err;
381	picl_prophdl_t	proph;
382	picl_propinfo_t	propinfo;
383	picl_prophdl_t	tblh;
384
385	for (err = picl_get_first_prop(nodeh, &proph); err == PICL_SUCCESS;
386	    err = picl_get_next_prop(proph, &proph)) {
387
388		err = picl_get_propinfo(proph, &propinfo);
389		if (err != PICL_SUCCESS) {
390			print_errmsg(gettext(err_msg[EM_GETPINFO]),
391			    picl_strerror(err));
392			return (err);
393		}
394
395		if (propinfo.type == PICL_PTYPE_VOID)
396			(void) printf("%*s:%s\n", PROPINFO_LEFT_MARGIN(lvl),
397			    " ", propinfo.name);
398		else {
399			(void) printf("%*s:%s\t", PROPINFO_LEFT_MARGIN(lvl),
400			    " ", propinfo.name);
401			err = print_propval(lvl, proph, &propinfo);
402			(void) printf("\n");
403			if (err != PICL_SUCCESS)
404				return (err);
405		}
406
407		/*
408		 * Expand the table property
409		 */
410		if (propinfo.type == PICL_PTYPE_TABLE) {
411			err = picl_get_propval(proph, &tblh, propinfo.size);
412			if (err != PICL_SUCCESS) {
413				print_errmsg(gettext(err_msg[EM_GETPVAL]),
414				    picl_strerror(err));
415				return (err);
416			}
417			err = print_table_prop(lvl, tblh);
418			if (err != PICL_SUCCESS)
419				return (err);
420		}
421	}
422	return (PICL_SUCCESS);
423}
424
425/*
426 * Recursively print the PICL tree
427 * When piclclass is specified, print only the nodes of that class.
428 */
429static int
430print_tree_by_class(int lvl, picl_nodehdl_t nodeh, char *piclclass)
431{
432	picl_nodehdl_t	chdh;
433	char		*nameval;
434	char		classval[PICL_PROPNAMELEN_MAX];
435	int		err;
436	picl_prophdl_t	proph;
437	picl_propinfo_t	pinfo;
438
439	/*
440	 * First get the class name of the node to compare with piclclass
441	 */
442	err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, classval,
443	    sizeof (classval));
444	if (err != PICL_SUCCESS) {
445		print_errmsg(gettext(err_msg[EM_GETPVALBYNAME]),
446		    picl_strerror(err));
447		return (err);
448	}
449
450#define	MATCHING_CLASSVAL(x, y)	((x == NULL) || (strcasecmp(x, y) == 0))
451
452	if (MATCHING_CLASSVAL(piclclass, classval)) {
453		err = picl_get_prop_by_name(nodeh, PICL_PROP_NAME, &proph);
454		if (err != PICL_SUCCESS) {
455			print_errmsg(gettext(err_msg[EM_GETPROPBYNAME]),
456			    picl_strerror(err));
457			return (err);
458		}
459
460		err = picl_get_propinfo(proph, &pinfo);
461		if (err != PICL_SUCCESS) {
462			print_errmsg(gettext(err_msg[EM_GETPINFO]),
463			    picl_strerror(err));
464			return (err);
465		}
466
467		nameval = alloca(pinfo.size);
468		err = picl_get_propval(proph, nameval, pinfo.size);
469		if (err != PICL_SUCCESS) {
470			print_errmsg(gettext(err_msg[EM_GETPVAL]),
471			    picl_strerror(err));
472			return (err);
473		}
474
475		(void) printf("%*s %s (%s, %" PRIxPICLHDL ")\n",
476		    NODEINFO_LEFT_MARGIN(lvl), " ", nameval, classval, nodeh);
477
478		if (verbose_mode) {
479			err = print_proplist(lvl, nodeh);
480			if (err != PICL_SUCCESS)
481				return (err);
482		}
483		++lvl;
484	}
485
486	for (err = picl_get_propval_by_name(nodeh, PICL_PROP_CHILD, &chdh,
487	    sizeof (picl_nodehdl_t)); err != PICL_PROPNOTFOUND;
488	    err = picl_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh,
489	    sizeof (picl_nodehdl_t))) {
490
491		if (err != PICL_SUCCESS) {
492			print_errmsg(gettext(err_msg[EM_GETPVALBYNAME]),
493			    picl_strerror(err));
494			return (err);
495		}
496
497		err = print_tree_by_class(lvl, chdh, piclclass);
498		if (err != PICL_SUCCESS)
499			return (err);
500	}
501	return (PICL_SUCCESS);
502}
503
504
505/*
506 * This program prints the PICL tree.
507 * If an invalid handle or stale handle is encountered while printing
508 * the tree, it starts over from the root node.
509 */
510int
511main(int argc, char **argv)
512{
513	int		err;
514	picl_nodehdl_t	rooth;
515	int		c;
516	int		done;
517	char		piclclass[PICL_CLASSNAMELEN_MAX];
518	int		cflg;
519
520	(void) setlocale(LC_ALL, "");
521	(void) textdomain(TEXT_DOMAIN);
522
523	if ((prog = strrchr(argv[0], '/')) == NULL)
524		prog = argv[0];
525	else
526		prog++;
527
528	cflg = 0;
529	while ((c = getopt(argc, argv, "vc:")) != EOF) {
530		switch (c) {
531		case 'v':
532			verbose_mode = 1;
533			break;
534		case 'c':
535			cflg = 1;
536			(void) strlcpy(piclclass, optarg,
537			    PICL_CLASSNAMELEN_MAX);
538			break;
539		case '?':
540			/*FALLTHROUGH*/
541		default:
542			usage();
543			/*NOTREACHED*/
544		}
545	}
546	if (optind != argc)
547		usage();
548
549	err = picl_initialize();
550	if (err != PICL_SUCCESS) {
551		print_errmsg(gettext(err_msg[EM_INIT]), picl_strerror(err));
552		exit(1);
553	}
554
555
556	do {
557		done = 1;
558		err = picl_get_root(&rooth);
559		if (err != PICL_SUCCESS) {
560			print_errmsg(gettext(err_msg[EM_GETROOT]),
561			    picl_strerror(err));
562			exit(1);
563		}
564
565		err = print_tree_by_class(ROOT_LEVEL, rooth,
566		    (cflg ? piclclass : NULL));
567		if ((err == PICL_STALEHANDLE) || (err == PICL_INVALIDHANDLE))
568			done = 0;
569	} while (!done);
570
571	(void) picl_shutdown();
572
573	return (0);
574}
575