xref: /illumos-gate/usr/src/cmd/diskinfo/diskinfo.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2013 Joyent Inc., All rights reserved.
14  */
15 
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <limits.h>
24 #include <assert.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 #include <strings.h>
28 
29 #include <libdiskmgt.h>
30 #include <sys/nvpair.h>
31 #include <sys/param.h>
32 #include <sys/ccompile.h>
33 
34 #include <fm/libtopo.h>
35 #include <fm/topo_hc.h>
36 #include <fm/topo_list.h>
37 #include <sys/fm/protocol.h>
38 #include <modules/common/disk/disk.h>
39 
40 typedef struct di_opts {
41 	boolean_t di_scripted;
42 	boolean_t di_parseable;
43 	boolean_t di_physical;
44 	boolean_t di_condensed;
45 } di_opts_t;
46 
47 typedef struct di_phys {
48 	const char *dp_dev;
49 	const char *dp_serial;
50 	const char *dp_slotname;
51 	int dp_chassis;
52 	int dp_slot;
53 	int dp_faulty;
54 	int dp_locate;
55 } di_phys_t;
56 
57 static void __NORETURN
58 fatal(int rv, const char *fmt, ...)
59 {
60 	va_list ap;
61 
62 	va_start(ap, fmt);
63 	(void) vfprintf(stderr, fmt, ap);
64 	va_end(ap);
65 
66 	exit(rv);
67 }
68 
69 static void
70 usage(const char *execname)
71 {
72 	(void) fprintf(stderr, "Usage: %s [-Hp] [{-c|-P}]\n", execname);
73 }
74 
75 static void
76 nvlist_query_string(nvlist_t *nvl, const char *label, char **val)
77 {
78 	if (nvlist_lookup_string(nvl, label, val) != 0)
79 		*val = "-";
80 }
81 
82 static const char *
83 display_string(const char *label)
84 {
85 	return ((label) ? label : "-");
86 }
87 
88 static const char *
89 display_tristate(int val)
90 {
91 	if (val == 0)
92 		return ("no");
93 	if (val == 1)
94 		return ("yes");
95 
96 	return ("-");
97 }
98 
99 static char
100 condensed_tristate(int val, char c)
101 {
102 	if (val == 0)
103 		return ('-');
104 	if (val == 1)
105 		return (c);
106 
107 	return ('?');
108 }
109 static int
110 disk_walker(topo_hdl_t *hp, tnode_t *np, void *arg)
111 {
112 	di_phys_t *pp = arg;
113 	tnode_t *pnp;
114 	tnode_t *ppnp;
115 	topo_faclist_t fl;
116 	topo_faclist_t *lp;
117 	int err;
118 	topo_led_state_t mode;
119 	topo_led_type_t type;
120 	char *name, *slotname, *serial;
121 
122 	if (strcmp(topo_node_name(np), DISK) != 0)
123 		return (TOPO_WALK_NEXT);
124 
125 	if (topo_prop_get_string(np, TOPO_PGROUP_STORAGE,
126 	    TOPO_STORAGE_LOGICAL_DISK_NAME, &name, &err) != 0) {
127 		return (TOPO_WALK_NEXT);
128 	}
129 
130 	if (strcmp(name, pp->dp_dev) != 0)
131 		return (TOPO_WALK_NEXT);
132 
133 	if (topo_prop_get_string(np, TOPO_PGROUP_STORAGE,
134 	    TOPO_STORAGE_SERIAL_NUM, &serial, &err) == 0) {
135 		pp->dp_serial = serial;
136 	}
137 
138 	pnp = topo_node_parent(np);
139 	ppnp = topo_node_parent(pnp);
140 	if (strcmp(topo_node_name(pnp), BAY) == 0) {
141 		if (topo_node_facility(hp, pnp, TOPO_FAC_TYPE_INDICATOR,
142 		    TOPO_FAC_TYPE_ANY, &fl, &err) == 0) {
143 			for (lp = topo_list_next(&fl.tf_list); lp != NULL;
144 			    lp = topo_list_next(lp)) {
145 				uint32_t prop;
146 
147 				if (topo_prop_get_uint32(lp->tf_node,
148 				    TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
149 				    &prop, &err) != 0) {
150 					continue;
151 				}
152 				type = (topo_led_type_t)prop;
153 
154 				if (topo_prop_get_uint32(lp->tf_node,
155 				    TOPO_PGROUP_FACILITY, TOPO_LED_MODE,
156 				    &prop, &err) != 0) {
157 					continue;
158 				}
159 				mode = (topo_led_state_t)prop;
160 
161 				switch (type) {
162 				case TOPO_LED_TYPE_SERVICE:
163 					pp->dp_faulty = mode ? 1 : 0;
164 					break;
165 				case TOPO_LED_TYPE_LOCATE:
166 					pp->dp_locate = mode ? 1 : 0;
167 					break;
168 				default:
169 					break;
170 				}
171 			}
172 		}
173 
174 		if (topo_prop_get_string(pnp, TOPO_PGROUP_PROTOCOL,
175 		    TOPO_PROP_LABEL, &slotname, &err) == 0) {
176 			pp->dp_slotname = slotname;
177 		}
178 
179 		pp->dp_slot = topo_node_instance(pnp);
180 	}
181 
182 	pp->dp_chassis = topo_node_instance(ppnp);
183 
184 	return (TOPO_WALK_TERMINATE);
185 }
186 
187 static void
188 populate_physical(topo_hdl_t *hp, di_phys_t *pp)
189 {
190 	int err;
191 	topo_walk_t *wp;
192 
193 	pp->dp_faulty = pp->dp_locate = -1;
194 	pp->dp_chassis = pp->dp_slot = -1;
195 
196 	err = 0;
197 	wp = topo_walk_init(hp, FM_FMRI_SCHEME_HC, disk_walker, pp, &err);
198 	if (wp == NULL) {
199 		fatal(-1, "unable to initialise topo walker: %s",
200 		    topo_strerror(err));
201 	}
202 
203 	while ((err = topo_walk_step(wp, TOPO_WALK_CHILD)) == TOPO_WALK_NEXT)
204 		;
205 
206 	if (err == TOPO_WALK_ERR)
207 		fatal(-1, "topo walk failed");
208 
209 	topo_walk_fini(wp);
210 }
211 
212 static void
213 enumerate_disks(di_opts_t *opts)
214 {
215 	topo_hdl_t *hp;
216 	dm_descriptor_t *media;
217 	int err, i;
218 	int filter[] = { DM_DT_FIXED, -1 };
219 	dm_descriptor_t *disk, *controller;
220 	nvlist_t *mattrs, *dattrs, *cattrs = NULL;
221 
222 	uint64_t size, total;
223 	uint32_t blocksize;
224 	double total_in_GiB;
225 	char sizestr[32];
226 	char slotname[32];
227 	char statestr[8];
228 
229 	char *vid, *pid, *opath, *c, *ctype = NULL;
230 	boolean_t removable;
231 	boolean_t ssd;
232 	char device[MAXPATHLEN];
233 	di_phys_t phys;
234 	size_t len;
235 
236 	err = 0;
237 	if ((media = dm_get_descriptors(DM_MEDIA, filter, &err)) == NULL) {
238 		fatal(-1, "failed to obtain media descriptors: %s\n",
239 		    strerror(err));
240 	}
241 
242 	err = 0;
243 	hp = topo_open(TOPO_VERSION, NULL, &err);
244 	if (hp == NULL) {
245 		fatal(-1, "unable to obtain topo handle: %s",
246 		    topo_strerror(err));
247 	}
248 
249 	err = 0;
250 	(void) topo_snap_hold(hp, NULL, &err);
251 	if (err != 0) {
252 		fatal(-1, "unable to hold topo snapshot: %s",
253 		    topo_strerror(err));
254 	}
255 
256 	for (i = 0; media != NULL && media[i] != NULL; i++) {
257 		if ((disk = dm_get_associated_descriptors(media[i],
258 		    DM_DRIVE, &err)) == NULL) {
259 			continue;
260 		}
261 
262 		mattrs = dm_get_attributes(media[i], &err);
263 		err = nvlist_lookup_uint64(mattrs, DM_SIZE, &size);
264 		assert(err == 0);
265 		err = nvlist_lookup_uint32(mattrs, DM_BLOCKSIZE, &blocksize);
266 		assert(err == 0);
267 		nvlist_free(mattrs);
268 
269 		dattrs = dm_get_attributes(disk[0], &err);
270 
271 		nvlist_query_string(dattrs, DM_VENDOR_ID, &vid);
272 		nvlist_query_string(dattrs, DM_PRODUCT_ID, &pid);
273 		nvlist_query_string(dattrs, DM_OPATH, &opath);
274 
275 		removable = B_FALSE;
276 		if (nvlist_lookup_boolean(dattrs, DM_REMOVABLE) == 0)
277 			removable = B_TRUE;
278 
279 		ssd = B_FALSE;
280 		if (nvlist_lookup_boolean(dattrs, DM_SOLIDSTATE) == 0)
281 			ssd = B_TRUE;
282 
283 		if ((controller = dm_get_associated_descriptors(disk[0],
284 		    DM_CONTROLLER, &err)) != NULL) {
285 			cattrs = dm_get_attributes(controller[0], &err);
286 			nvlist_query_string(cattrs, DM_CTYPE, &ctype);
287 			ctype = strdup(ctype);
288 			for (c = ctype; *c != '\0'; c++)
289 				*c = toupper(*c);
290 		}
291 
292 		/*
293 		 * Parse full device path to only show the device name,
294 		 * i.e. c0t1d0.  Many paths will reference a particular
295 		 * slice (c0t1d0s0), so remove the slice if present.
296 		 */
297 		if ((c = strrchr(opath, '/')) != NULL)
298 			(void) strlcpy(device, c + 1, sizeof (device));
299 		else
300 			(void) strlcpy(device, opath, sizeof (device));
301 		len = strlen(device);
302 		if (device[len - 2] == 's' &&
303 		    (device[len - 1] >= '0' && device[len - 1] <= '9'))
304 			device[len - 2] = '\0';
305 
306 		bzero(&phys, sizeof (phys));
307 		phys.dp_dev = device;
308 		populate_physical(hp, &phys);
309 
310 		/*
311 		 * The size is given in blocks, so multiply the number
312 		 * of blocks by the block size to get the total size,
313 		 * then convert to GiB.
314 		 */
315 		total = size * blocksize;
316 
317 		if (opts->di_parseable) {
318 			(void) snprintf(sizestr, sizeof (sizestr),
319 			    "%llu", total);
320 		} else {
321 			total_in_GiB = (double)total /
322 			    1024.0 / 1024.0 / 1024.0;
323 			(void) snprintf(sizestr, sizeof (sizestr),
324 			    "%7.2f GiB", total_in_GiB);
325 		}
326 
327 		if (opts->di_parseable) {
328 			(void) snprintf(slotname, sizeof (slotname), "%d,%d",
329 			    phys.dp_chassis, phys.dp_slot);
330 		} else if (phys.dp_slotname != NULL) {
331 			(void) snprintf(slotname, sizeof (slotname),
332 			    "[%d] %s", phys.dp_chassis, phys.dp_slotname);
333 		} else {
334 			slotname[0] = '-';
335 			slotname[1] = '\0';
336 		}
337 
338 		if (opts->di_condensed) {
339 			(void) snprintf(statestr, sizeof (statestr), "%c%c%c%c",
340 			    condensed_tristate(phys.dp_faulty, 'F'),
341 			    condensed_tristate(phys.dp_locate, 'L'),
342 			    condensed_tristate(removable, 'R'),
343 			    condensed_tristate(ssd, 'S'));
344 		}
345 
346 		if (opts->di_physical) {
347 			if (opts->di_scripted) {
348 				printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
349 				    device, vid, pid,
350 				    display_string(phys.dp_serial),
351 				    display_tristate(phys.dp_faulty),
352 				    display_tristate(phys.dp_locate), slotname);
353 			} else {
354 				printf("%-22s  %-8s %-16s "
355 				    "%-20s %-3s %-3s %s\n",
356 				    device, vid, pid,
357 				    display_string(phys.dp_serial),
358 				    display_tristate(phys.dp_faulty),
359 				    display_tristate(phys.dp_locate), slotname);
360 			}
361 		} else if (opts->di_condensed) {
362 			if (opts->di_scripted) {
363 				printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
364 				    ctype, device, vid, pid,
365 				    display_string(phys.dp_serial),
366 				    sizestr, statestr, slotname);
367 			} else {
368 				printf("%-7s %-22s  %-8s %-16s "
369 				    "%-20s\n\t%-13s %-4s %s\n",
370 				    ctype, device, vid, pid,
371 				    display_string(phys.dp_serial),
372 				    sizestr, statestr, slotname);
373 			}
374 		} else {
375 			if (opts->di_scripted) {
376 				printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
377 				    ctype, device, vid, pid, sizestr,
378 				    display_tristate(removable),
379 				    display_tristate(ssd));
380 			} else {
381 				printf("%-7s %-22s  %-8s %-16s "
382 				    "%-13s %-3s %-3s\n", ctype, device,
383 				    vid, pid, sizestr,
384 				    display_tristate(removable),
385 				    display_tristate(ssd));
386 			}
387 		}
388 
389 		free(ctype);
390 		nvlist_free(cattrs);
391 		nvlist_free(dattrs);
392 		dm_free_descriptors(controller);
393 		dm_free_descriptors(disk);
394 	}
395 
396 	dm_free_descriptors(media);
397 	topo_snap_release(hp);
398 	topo_close(hp);
399 }
400 
401 int
402 main(int argc, char *argv[])
403 {
404 	char c;
405 
406 	di_opts_t opts = {
407 		.di_condensed = B_FALSE,
408 		.di_scripted = B_FALSE,
409 		.di_physical = B_FALSE,
410 		.di_parseable = B_FALSE
411 	};
412 
413 	while ((c = getopt(argc, argv, ":cHPp")) != EOF) {
414 		switch (c) {
415 		case 'c':
416 			if (opts.di_physical) {
417 				usage(argv[0]);
418 				fatal(1, "-c and -P are mutually exclusive\n");
419 			}
420 			opts.di_condensed = B_TRUE;
421 			break;
422 		case 'H':
423 			opts.di_scripted = B_TRUE;
424 			break;
425 		case 'P':
426 			if (opts.di_condensed) {
427 				usage(argv[0]);
428 				fatal(1, "-c and -P are mutually exclusive\n");
429 			}
430 			opts.di_physical = B_TRUE;
431 			break;
432 		case 'p':
433 			opts.di_parseable = B_TRUE;
434 			break;
435 		case '?':
436 			usage(argv[0]);
437 			fatal(1, "unknown option -%c\n", optopt);
438 		default:
439 			fatal(-1, "unexpected error on option -%c\n", optopt);
440 		}
441 	}
442 
443 	if (!opts.di_scripted) {
444 		if (opts.di_physical) {
445 			printf("DISK                    VID      PID"
446 			    "              SERIAL               FLT LOC"
447 			    " LOCATION\n");
448 		} else if (opts.di_condensed) {
449 			printf("TYPE    DISK                    VID      PID"
450 			    "              SERIAL\n");
451 			printf("\tSIZE          FLRS LOCATION\n");
452 		} else {
453 			printf("TYPE    DISK                    VID      PID"
454 			    "              SIZE          RMV SSD\n");
455 		}
456 	}
457 
458 	enumerate_disks(&opts);
459 
460 	return (0);
461 }
462