1/*	$OpenBSD: optree.c,v 1.11 2021/10/24 21:24:18 deraadt Exp $	*/
2
3/*
4 * Copyright (c) 2007 Federico G. Schwindt <fgsch@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for
7 * any purpose with or without fee is hereby granted, provided that
8 * the above copyright notice and this permission notice appear in all
9 * copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
12 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
15 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
16 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
17 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18 * PERFORMANCE OF THIS SOFTWARE.
19 */
20
21#include <sys/ioctl.h>
22#include <err.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <stdio.h>
26#include <string.h>
27#include <unistd.h>
28
29#include <machine/openpromio.h>
30
31#include "defs.h"
32
33extern  char *path_openprom;
34
35static void
36op_print(struct opiocdesc *opio, int depth)
37{
38	char *p;
39	int i, multi, special;
40	uint32_t cell;
41
42	opio->op_name[opio->op_namelen] = '\0';
43	printf("%*s%s: ", depth * 4, " ", opio->op_name);
44	if (opio->op_buflen > 0) {
45		opio->op_buf[opio->op_buflen] = '\0';
46		multi = special = 0;
47
48		/*
49		 * On macppc we have string-values properties that end
50		 * with multiple NUL characters, and the serial number
51		 * has them embedded within the string.
52		 */
53		if (opio->op_buf[0] != '\0') {
54			for (i = 0; i < opio->op_buflen; i++) {
55				p = &opio->op_buf[i];
56				if (*p >= ' ' && *p <= '~')
57					continue;
58				if (*p == '\0') {
59					if (i + 1 < opio->op_buflen)
60						p++;
61					if (*p >= ' ' && *p <= '~') {
62						special = multi;
63						continue;
64					}
65					if (*p == '\0') {
66						multi = 1;
67						continue;
68					}
69				}
70
71				special = 1;
72				break;
73			}
74		} else {
75			if (opio->op_buflen > 1)
76				special = 1;
77		}
78
79		if (special && strcmp(opio->op_name, "serial-number") != 0) {
80			for (i = 0; opio->op_buflen - i >= sizeof(int);
81			    i += sizeof(int)) {
82				if (i)
83					printf(".");
84				cell = *(uint32_t *)&opio->op_buf[i];
85				printf("%08x", betoh32(cell));
86			}
87			if (i < opio->op_buflen) {
88				if (i)
89					printf(".");
90				for (; i < opio->op_buflen; i++) {
91					printf("%02x",
92					    *(u_char *)&opio->op_buf[i]);
93				}
94			}
95		} else {
96			for (i = 0; i < opio->op_buflen;
97			    i += strlen(&opio->op_buf[i]) + 1) {
98				if (i && strlen(&opio->op_buf[i]) == 0)
99					continue;
100				if (i)
101					printf(" + ");
102				printf("'%s'", &opio->op_buf[i]);
103			}
104		}
105	} else if(opio->op_buflen < 0)
106		printf("too large");
107	printf("\n");
108}
109
110void
111op_nodes(int fd, int node, int depth)
112{
113	char op_buf[BUFSIZE * 8];
114	char op_name[BUFSIZE];
115	struct opiocdesc opio;
116
117	memset(op_name, 0, sizeof(op_name));
118	opio.op_nodeid = node;
119	opio.op_buf = op_buf;
120	opio.op_name = op_name;
121
122	if (!node) {
123		if (ioctl(fd, OPIOCGETNEXT, &opio) == -1)
124			err(1, "OPIOCGETNEXT");
125		node = opio.op_nodeid;
126	} else
127		printf("\n%*s", depth * 4, " ");
128
129	printf("Node 0x%x\n", node);
130
131	for (;;) {
132		opio.op_buflen = sizeof(op_buf);
133		opio.op_namelen = sizeof(op_name);
134
135		/* Get the next property. */
136		if (ioctl(fd, OPIOCNEXTPROP, &opio) == -1)
137			err(1, "OPIOCNEXTPROP");
138
139		op_buf[opio.op_buflen] = '\0';
140		(void)strlcpy(op_name, op_buf, sizeof(op_name));
141		opio.op_namelen = strlen(op_name);
142
143		/* If it's the last, punt. */
144		if (opio.op_namelen == 0)
145			break;
146
147		bzero(op_buf, sizeof(op_buf));
148		opio.op_buflen = sizeof(op_buf);
149
150		/* And its value. */
151		if (ioctl(fd, OPIOCGET, &opio) == -1) {
152			if (errno != ENOMEM)
153				err(1, "OPIOCGET");
154
155			opio.op_buflen = -1;	/* for op_print */
156		}
157
158		op_print(&opio, depth + 1);
159	}
160
161	/* Get next child. */
162	if (ioctl(fd, OPIOCGETCHILD, &opio) == -1)
163		err(1, "OPIOCGETCHILD");
164	if (opio.op_nodeid)
165		op_nodes(fd, opio.op_nodeid, depth + 1);
166
167	/* Get next node/sibling. */
168	opio.op_nodeid = node;
169	if (ioctl(fd, OPIOCGETNEXT, &opio) == -1)
170		err(1, "OPIOCGETNEXT");
171	if (opio.op_nodeid)
172		op_nodes(fd, opio.op_nodeid, depth);
173}
174
175void
176op_tree(void)
177{
178	int fd;
179
180	if ((fd = open(path_openprom, O_RDONLY)) == -1)
181		err(1, "open: %s", path_openprom);
182	op_nodes(fd, 0, 0);
183	(void)close(fd);
184}
185