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