xref: /illumos-gate/usr/src/cmd/prtconf/prtconf.c (revision 7038abe7532205428646f2442895374489b1c307)
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 /*
23  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2011, Joyent, Inc. All rights reserved.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <strings.h>
35 #include <sys/systeminfo.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include "prtconf.h"
39 
40 struct prt_opts	opts;
41 struct prt_dbg	dbg;
42 static char	new_path[MAXPATHLEN];
43 
44 #define	INDENT_LENGTH	4
45 
46 #ifdef	__x86
47 static const char *usage = "%s [ -V | -x | -abcdvpPD ] [ <device_path > ]\n";
48 #else
49 static const char *usage =
50 	"%s [ -F | -V | -x | -abcdvpPD ][ <device_path > ]\n";
51 #endif	/* __x86 */
52 
53 static void
54 setpname(const char *name)
55 {
56 	char *p;
57 
58 	if (name == NULL)
59 		opts.o_progname = "prtconf";
60 	else if (p = strrchr(name, '/'))
61 		opts.o_progname = (const char *) p + 1;
62 	else
63 		opts.o_progname = name;
64 }
65 
66 /*PRINTFLIKE1*/
67 void
68 dprintf(const char *fmt, ...)
69 {
70 	if (dbg.d_debug) {
71 		va_list ap;
72 		va_start(ap, fmt);
73 		(void) vfprintf(stderr, fmt, ap);
74 		va_end(ap);
75 	}
76 }
77 
78 void
79 indent_to_level(int ilev)
80 {
81 	(void) printf("%*s", INDENT_LENGTH * ilev, "");
82 }
83 
84 static void
85 cleanup_path(const char *input_path, char *path)
86 {
87 	char	*ptr, *ptr2;
88 	size_t	len;
89 
90 	if ((input_path == NULL) || (path == NULL))
91 		return;
92 
93 	(void) strcpy(path, input_path);
94 
95 	/*LINTED*/
96 	while (1) {
97 		len = strlen(path);
98 		if (len == 0)
99 			break;
100 
101 		/* change substring "//" into "/" */
102 		if (ptr = strstr(path, "//")) {
103 			len = strlen(ptr + 1);
104 			(void) memmove(ptr, ptr + 1, len + 1);
105 			continue;
106 		}
107 		/* change substring "/./" into "/" */
108 		if (ptr = strstr(path, "/./")) {
109 			len = strlen(ptr + 2);
110 			(void) memmove(ptr, ptr + 2, len + 1);
111 			continue;
112 		}
113 
114 		/* change substring "/<foo>/../" into "/" */
115 		if (ptr = strstr(path, "/../")) {
116 			len = strlen(ptr + 3);
117 			*ptr = '\0';
118 			ptr2 = strrchr(path, (int)'/');
119 			if (ptr2 == NULL) {
120 				/* path had a leading "/../" */
121 				ptr2 = path;
122 			}
123 			(void) memmove(ptr2, ptr + 3, len + 1);
124 			continue;
125 		}
126 
127 		/* change trailing "/<foo>/.." into "/" */
128 		if ((len >= 3) &&
129 		    (path[len - 3] == '/') &&
130 		    (path[len - 2] == '.') &&
131 		    (path[len - 1] == '.')) {
132 			path[len - 3] = '\0';
133 			ptr2 = strrchr(path, (int)'/');
134 			if (ptr2 != NULL) {
135 				ptr2[1] = '\0';
136 			} else {
137 				/* path was "/.." */
138 				path[0] = '/';
139 				path[1] = '\0';
140 			}
141 			continue;
142 		}
143 
144 		/* change trailing "/." into "/" */
145 		if ((len >= 2) &&
146 		    (path[len - 2] == '/') &&
147 		    (path[len - 1] == '.')) {
148 			path[len - 1] = '\0';
149 			continue;
150 		}
151 
152 		/* remove trailing "/" unless it's the root */
153 		if ((len > 1) && (path[len - 1] == '/')) {
154 			path[len - 1] = '\0';
155 			continue;
156 		}
157 
158 		break;
159 	}
160 }
161 
162 
163 /*
164  * debug version has two more flags:
165  *	-L force load driver
166  *	-M: print per driver list
167  */
168 
169 #ifdef	DEBUG
170 static const char *optstring = "abcdDvVxmpPFf:M:dLuC";
171 #else
172 static const char *optstring = "abcdDvVxmpPFf:uC";
173 #endif	/* DEBUG */
174 
175 int
176 main(int argc, char *argv[])
177 {
178 	long pagesize, npages;
179 	int c, ret;
180 	char hw_provider[SYS_NMLN];
181 
182 	setpname(argv[0]);
183 	opts.o_promdev = "/dev/openprom";
184 
185 	while ((c = getopt(argc, argv, optstring)) != -1)  {
186 		switch (c)  {
187 		case 'a':
188 			++opts.o_ancestors;
189 			break;
190 		case 'b':
191 			++opts.o_productinfo;
192 			break;
193 		case 'c':
194 			++opts.o_children;
195 			break;
196 		case 'd':
197 			++opts.o_pciid;
198 			break;
199 		case 'D':
200 			++opts.o_drv_name;
201 			break;
202 		case 'v':
203 			++opts.o_verbose;
204 			break;
205 		case 'm':
206 			++opts.o_memory;
207 			break;
208 		case 'p':
209 			++opts.o_prominfo;
210 			break;
211 		case 'f':
212 			opts.o_promdev = optarg;
213 			break;
214 		case 'V':
215 			++opts.o_promversion;
216 			break;
217 		case 'x':
218 			++opts.o_prom_ready64;
219 			break;
220 		case 'F':
221 			++opts.o_fbname;
222 			++opts.o_noheader;
223 			break;
224 		case 'P':
225 			++opts.o_pseudodevs;
226 			break;
227 		case 'C':
228 			++opts.o_forcecache;
229 			break;
230 #ifdef	DEBUG
231 		case 'M':
232 			dbg.d_drivername = optarg;
233 			++dbg.d_bydriver;
234 			break;
235 		case 'L':
236 			++dbg.d_forceload;
237 			break;
238 #endif	/* DEBUG */
239 
240 		default:
241 			(void) fprintf(stderr, usage, opts.o_progname);
242 			return (1);
243 		}
244 	}
245 
246 	(void) uname(&opts.o_uts);
247 
248 	if (opts.o_fbname)
249 		return (do_fbname());
250 
251 	if (opts.o_promversion)
252 		return (do_promversion());
253 
254 	if (opts.o_prom_ready64)
255 		return (do_prom_version64());
256 
257 	if (opts.o_productinfo)
258 		return (do_productinfo());
259 
260 	opts.o_devices_path = NULL;
261 	opts.o_devt = DDI_DEV_T_NONE;
262 	opts.o_target = 0;
263 	if (optind < argc) {
264 		struct stat	sinfo;
265 		char		*path = argv[optind];
266 		int		error;
267 
268 		if (opts.o_prominfo) {
269 			/* PROM tree cannot be used with path */
270 			(void) fprintf(stderr, "%s: path and -p option are "
271 			    "mutually exclusive\n", opts.o_progname);
272 			return (1);
273 		}
274 
275 		if (strlen(path) >= MAXPATHLEN) {
276 			(void) fprintf(stderr, "%s: "
277 			    "path specified is too long\n", opts.o_progname);
278 			return (1);
279 		}
280 
281 		if (error = stat(path, &sinfo)) {
282 
283 			/* an invalid path was specified */
284 			(void) fprintf(stderr, "%s: invalid path specified\n",
285 			    opts.o_progname);
286 			return (1);
287 
288 		} else if (((sinfo.st_mode & S_IFMT) == S_IFCHR) ||
289 		    ((sinfo.st_mode & S_IFMT) == S_IFBLK)) {
290 
291 			opts.o_devt = sinfo.st_rdev;
292 			error = 0;
293 
294 		} else if ((sinfo.st_mode & S_IFMT) == S_IFDIR) {
295 			size_t	len, plen;
296 
297 			/* clean up the path */
298 			cleanup_path(path, new_path);
299 
300 			len = strlen(new_path);
301 			plen = strlen("/devices");
302 			if (len < plen) {
303 				/* This is not a valid /devices path */
304 				error = 1;
305 			} else if ((len == plen) &&
306 			    (strcmp(new_path, "/devices") == 0)) {
307 				/* /devices is the root nexus */
308 				opts.o_devices_path = "/";
309 				error = 0;
310 			} else if (strncmp(new_path, "/devices/", plen + 1)) {
311 				/* This is not a valid /devices path */
312 				error = 1;
313 			} else {
314 				/* a /devices/ path was specified */
315 				opts.o_devices_path = new_path + plen;
316 				error = 0;
317 			}
318 
319 		} else {
320 			/* an invalid device path was specified */
321 			error = 1;
322 		}
323 
324 		if (error) {
325 			(void) fprintf(stderr, "%s: "
326 			    "invalid device path specified\n",
327 			    opts.o_progname);
328 			return (1);
329 		}
330 
331 		opts.o_target = 1;
332 	}
333 
334 	if ((opts.o_ancestors || opts.o_children) && (!opts.o_target)) {
335 		(void) fprintf(stderr, "%s: options require a device path\n",
336 		    opts.o_progname);
337 		return (1);
338 	}
339 
340 	if (opts.o_target) {
341 		prtconf_devinfo();
342 		return (0);
343 	}
344 
345 	if (!opts.o_memory) {
346 		ret = sysinfo(SI_HW_PROVIDER, hw_provider,
347 		    sizeof (hw_provider));
348 		/*
349 		 * If 0 bytes are returned (the system returns '1', for the \0),
350 		 * we're probably on x86, default to "Unknown Hardware Vendor".
351 		 */
352 		if (ret <= 1) {
353 			(void) strncpy(hw_provider, "Unknown Hardware Vendor",
354 			    sizeof (hw_provider));
355 		}
356 		(void) printf("System Configuration:  %s  %s\n", hw_provider,
357 		    opts.o_uts.machine);
358 	}
359 
360 	pagesize = sysconf(_SC_PAGESIZE);
361 	npages = sysconf(_SC_PHYS_PAGES);
362 	if (pagesize == -1 || npages == -1) {
363 		if (opts.o_memory) {
364 			(void) printf("0\n");
365 			return (1);
366 		} else {
367 			(void) printf("Memory size: unable to determine\n");
368 		}
369 	} else {
370 		const int64_t mbyte = 1024 * 1024;
371 		int64_t ii = (int64_t)pagesize * npages;
372 
373 		if (opts.o_memory) {
374 			(void) printf("%ld\n", (long)((ii+mbyte-1) / mbyte));
375 			return (0);
376 		} else {
377 			(void) printf("Memory size: %ld Megabytes\n",
378 			    (long)((ii+mbyte-1) / mbyte));
379 		}
380 	}
381 
382 	if (opts.o_prominfo) {
383 		(void) printf("System Peripherals (PROM Nodes):\n\n");
384 		if (do_prominfo() == 0)
385 			return (0);
386 		(void) fprintf(stderr, "%s: Defaulting to non-PROM mode...\n",
387 		    opts.o_progname);
388 	}
389 
390 	(void) printf("System Peripherals (Software Nodes):\n\n");
391 
392 	(void) prtconf_devinfo();
393 
394 	return (0);
395 }
396