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