xref: /illumos-gate/usr/src/cmd/prtconf/prtconf.c (revision 20c1c3551cb3b3117591ae38463d16aada597c48)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved	*/
28 
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 setprogname(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 = "abcdDvVxpPFf:M:dLuC";
171 #else
172 static const char *optstring = "abcdDvVxpPFf: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 	setprogname(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 '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 (do_prom_version64());
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 	ret = sysinfo(SI_HW_PROVIDER, hw_provider, sizeof (hw_provider));
343 	/*
344 	 * If 0 bytes are returned (the system returns '1', for the \0),
345 	 * we're probably on x86, and there has been no si-hw-provider
346 	 * set in /etc/bootrc, so just default to Sun.
347 	 */
348 	if (ret <= 1) {
349 		(void) strncpy(hw_provider, "Sun Microsystems",
350 		    sizeof (hw_provider));
351 	} else {
352 		/*
353 		 * Provide backward compatibility by stripping out the _.
354 		 */
355 		if (strcmp(hw_provider, "Sun_Microsystems") == 0)
356 			hw_provider[3] = ' ';
357 	}
358 	(void) printf("System Configuration:  %s  %s\n", hw_provider,
359 	    opts.o_uts.machine);
360 
361 	pagesize = sysconf(_SC_PAGESIZE);
362 	npages = sysconf(_SC_PHYS_PAGES);
363 	(void) printf("Memory size: ");
364 	if (pagesize == -1 || npages == -1)
365 		(void) printf("unable to determine\n");
366 	else {
367 		const int64_t kbyte = 1024;
368 		const int64_t mbyte = 1024 * 1024;
369 		int64_t ii = (int64_t)pagesize * npages;
370 
371 		if (ii >= mbyte)
372 			(void) printf("%ld Megabytes\n",
373 			    (long)((ii+mbyte-1) / mbyte));
374 		else
375 			(void) printf("%ld Kilobytes\n",
376 			    (long)((ii+kbyte-1) / kbyte));
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