xref: /illumos-gate/usr/src/cmd/diskinfo/diskinfo.c (revision 8a2b682e57a046b828f37bcde1776f131ef4629f)
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) 2018 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 	pp->dp_chassis = topo_node_instance(ppnp);
141 	if (strcmp(topo_node_name(pnp), BAY) == 0) {
142 		if (topo_node_facility(hp, pnp, TOPO_FAC_TYPE_INDICATOR,
143 		    TOPO_FAC_TYPE_ANY, &fl, &err) == 0) {
144 			for (lp = topo_list_next(&fl.tf_list); lp != NULL;
145 			    lp = topo_list_next(lp)) {
146 				uint32_t prop;
147 
148 				if (topo_prop_get_uint32(lp->tf_node,
149 				    TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
150 				    &prop, &err) != 0) {
151 					continue;
152 				}
153 				type = (topo_led_type_t)prop;
154 
155 				if (topo_prop_get_uint32(lp->tf_node,
156 				    TOPO_PGROUP_FACILITY, TOPO_LED_MODE,
157 				    &prop, &err) != 0) {
158 					continue;
159 				}
160 				mode = (topo_led_state_t)prop;
161 
162 				switch (type) {
163 				case TOPO_LED_TYPE_SERVICE:
164 					pp->dp_faulty = mode ? 1 : 0;
165 					break;
166 				case TOPO_LED_TYPE_LOCATE:
167 					pp->dp_locate = mode ? 1 : 0;
168 					break;
169 				default:
170 					break;
171 				}
172 			}
173 		}
174 
175 		if (topo_prop_get_string(pnp, TOPO_PGROUP_PROTOCOL,
176 		    TOPO_PROP_LABEL, &slotname, &err) == 0) {
177 			pp->dp_slotname = slotname;
178 		}
179 
180 		pp->dp_slot = topo_node_instance(pnp);
181 	} else if (strcmp(topo_node_name(pnp), USB_DEVICE) == 0) {
182 		if (topo_prop_get_string(pnp, TOPO_PGROUP_PROTOCOL,
183 		    TOPO_PROP_LABEL, &slotname, &err) == 0) {
184 			pp->dp_slotname = slotname;
185 		}
186 
187 		/*
188 		 * Override dp_chassis for USB devices since they show up
189 		 * everywhere in the name space and may not be under a logical
190 		 * bay.
191 		 */
192 		pp->dp_chassis = -1;
193 	}
194 
195 	return (TOPO_WALK_TERMINATE);
196 }
197 
198 static void
199 populate_physical(topo_hdl_t *hp, di_phys_t *pp)
200 {
201 	int err;
202 	topo_walk_t *wp;
203 
204 	pp->dp_faulty = pp->dp_locate = -1;
205 	pp->dp_chassis = pp->dp_slot = -1;
206 
207 	err = 0;
208 	wp = topo_walk_init(hp, FM_FMRI_SCHEME_HC, disk_walker, pp, &err);
209 	if (wp == NULL) {
210 		fatal(-1, "unable to initialise topo walker: %s",
211 		    topo_strerror(err));
212 	}
213 
214 	while ((err = topo_walk_step(wp, TOPO_WALK_CHILD)) == TOPO_WALK_NEXT)
215 		;
216 
217 	if (err == TOPO_WALK_ERR)
218 		fatal(-1, "topo walk failed");
219 
220 	topo_walk_fini(wp);
221 }
222 
223 static void
224 enumerate_disks(di_opts_t *opts)
225 {
226 	topo_hdl_t *hp;
227 	dm_descriptor_t *media;
228 	int err, i;
229 	int filter[] = { DM_DT_FIXED, -1 };
230 	dm_descriptor_t *disk, *controller;
231 	nvlist_t *mattrs, *dattrs, *cattrs = NULL;
232 
233 	uint64_t size, total;
234 	uint32_t blocksize;
235 	double total_in_GiB;
236 	char sizestr[32];
237 	char slotname[32];
238 	char statestr[8];
239 
240 	char *vid, *pid, *opath, *c, *ctype = NULL;
241 	boolean_t removable;
242 	boolean_t ssd;
243 	char device[MAXPATHLEN];
244 	di_phys_t phys;
245 	size_t len;
246 
247 	err = 0;
248 	if ((media = dm_get_descriptors(DM_MEDIA, filter, &err)) == NULL) {
249 		fatal(-1, "failed to obtain media descriptors: %s\n",
250 		    strerror(err));
251 	}
252 
253 	err = 0;
254 	hp = topo_open(TOPO_VERSION, NULL, &err);
255 	if (hp == NULL) {
256 		fatal(-1, "unable to obtain topo handle: %s",
257 		    topo_strerror(err));
258 	}
259 
260 	err = 0;
261 	(void) topo_snap_hold(hp, NULL, &err);
262 	if (err != 0) {
263 		fatal(-1, "unable to hold topo snapshot: %s",
264 		    topo_strerror(err));
265 	}
266 
267 	for (i = 0; media != NULL && media[i] != 0; i++) {
268 		if ((disk = dm_get_associated_descriptors(media[i],
269 		    DM_DRIVE, &err)) == NULL) {
270 			continue;
271 		}
272 
273 		mattrs = dm_get_attributes(media[i], &err);
274 		err = nvlist_lookup_uint64(mattrs, DM_SIZE, &size);
275 		assert(err == 0);
276 		err = nvlist_lookup_uint32(mattrs, DM_BLOCKSIZE, &blocksize);
277 		assert(err == 0);
278 		nvlist_free(mattrs);
279 
280 		dattrs = dm_get_attributes(disk[0], &err);
281 
282 		nvlist_query_string(dattrs, DM_VENDOR_ID, &vid);
283 		nvlist_query_string(dattrs, DM_PRODUCT_ID, &pid);
284 		nvlist_query_string(dattrs, DM_OPATH, &opath);
285 
286 		removable = B_FALSE;
287 		if (nvlist_lookup_boolean(dattrs, DM_REMOVABLE) == 0)
288 			removable = B_TRUE;
289 
290 		ssd = B_FALSE;
291 		if (nvlist_lookup_boolean(dattrs, DM_SOLIDSTATE) == 0)
292 			ssd = B_TRUE;
293 
294 		if ((controller = dm_get_associated_descriptors(disk[0],
295 		    DM_CONTROLLER, &err)) != NULL) {
296 			cattrs = dm_get_attributes(controller[0], &err);
297 			nvlist_query_string(cattrs, DM_CTYPE, &ctype);
298 			ctype = strdup(ctype);
299 			for (c = ctype; *c != '\0'; c++)
300 				*c = toupper(*c);
301 		}
302 
303 		/*
304 		 * Parse full device path to only show the device name,
305 		 * i.e. c0t1d0.  Many paths will reference a particular
306 		 * slice (c0t1d0s0), so remove the slice if present.
307 		 */
308 		if ((c = strrchr(opath, '/')) != NULL)
309 			(void) strlcpy(device, c + 1, sizeof (device));
310 		else
311 			(void) strlcpy(device, opath, sizeof (device));
312 		len = strlen(device);
313 		if (device[len - 2] == 's' &&
314 		    (device[len - 1] >= '0' && device[len - 1] <= '9'))
315 			device[len - 2] = '\0';
316 
317 		bzero(&phys, sizeof (phys));
318 		phys.dp_dev = device;
319 		populate_physical(hp, &phys);
320 
321 		/*
322 		 * The size is given in blocks, so multiply the number
323 		 * of blocks by the block size to get the total size,
324 		 * then convert to GiB.
325 		 */
326 		total = size * blocksize;
327 
328 		if (opts->di_parseable) {
329 			(void) snprintf(sizestr, sizeof (sizestr),
330 			    "%llu", total);
331 		} else {
332 			total_in_GiB = (double)total /
333 			    1024.0 / 1024.0 / 1024.0;
334 			(void) snprintf(sizestr, sizeof (sizestr),
335 			    "%7.2f GiB", total_in_GiB);
336 		}
337 
338 		if (opts->di_parseable) {
339 			(void) snprintf(slotname, sizeof (slotname), "%d,%d",
340 			    phys.dp_chassis, phys.dp_slot);
341 		} else if (phys.dp_slotname != NULL && phys.dp_chassis != -1) {
342 			(void) snprintf(slotname, sizeof (slotname),
343 			    "[%d] %s", phys.dp_chassis, phys.dp_slotname);
344 		} else if (phys.dp_slotname != NULL) {
345 			(void) snprintf(slotname, sizeof (slotname),
346 			    "%s", phys.dp_slotname);
347 		} else {
348 			slotname[0] = '-';
349 			slotname[1] = '\0';
350 		}
351 
352 		if (opts->di_condensed) {
353 			(void) snprintf(statestr, sizeof (statestr), "%c%c%c%c",
354 			    condensed_tristate(phys.dp_faulty, 'F'),
355 			    condensed_tristate(phys.dp_locate, 'L'),
356 			    condensed_tristate(removable, 'R'),
357 			    condensed_tristate(ssd, 'S'));
358 		}
359 
360 		if (opts->di_physical) {
361 			if (opts->di_scripted) {
362 				printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
363 				    device, vid, pid,
364 				    display_string(phys.dp_serial),
365 				    display_tristate(phys.dp_faulty),
366 				    display_tristate(phys.dp_locate), slotname);
367 			} else {
368 				printf("%-22s  %-8s %-16s "
369 				    "%-20s %-3s %-3s %s\n",
370 				    device, vid, pid,
371 				    display_string(phys.dp_serial),
372 				    display_tristate(phys.dp_faulty),
373 				    display_tristate(phys.dp_locate), slotname);
374 			}
375 		} else if (opts->di_condensed) {
376 			if (opts->di_scripted) {
377 				printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
378 				    ctype, device, vid, pid,
379 				    display_string(phys.dp_serial),
380 				    sizestr, statestr, slotname);
381 			} else {
382 				printf("%-7s %-22s  %-8s %-16s "
383 				    "%-20s\n\t%-13s %-4s %s\n",
384 				    ctype, device, vid, pid,
385 				    display_string(phys.dp_serial),
386 				    sizestr, statestr, slotname);
387 			}
388 		} else {
389 			if (opts->di_scripted) {
390 				printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
391 				    ctype, device, vid, pid, sizestr,
392 				    display_tristate(removable),
393 				    display_tristate(ssd));
394 			} else {
395 				printf("%-7s %-22s  %-8s %-16s "
396 				    "%-13s %-3s %-3s\n", ctype, device,
397 				    vid, pid, sizestr,
398 				    display_tristate(removable),
399 				    display_tristate(ssd));
400 			}
401 		}
402 
403 		free(ctype);
404 		nvlist_free(cattrs);
405 		nvlist_free(dattrs);
406 		dm_free_descriptors(controller);
407 		dm_free_descriptors(disk);
408 	}
409 
410 	dm_free_descriptors(media);
411 	topo_snap_release(hp);
412 	topo_close(hp);
413 }
414 
415 int
416 main(int argc, char *argv[])
417 {
418 	char c;
419 
420 	di_opts_t opts = {
421 		.di_condensed = B_FALSE,
422 		.di_scripted = B_FALSE,
423 		.di_physical = B_FALSE,
424 		.di_parseable = B_FALSE
425 	};
426 
427 	while ((c = getopt(argc, argv, ":cHPp")) != EOF) {
428 		switch (c) {
429 		case 'c':
430 			if (opts.di_physical) {
431 				usage(argv[0]);
432 				fatal(1, "-c and -P are mutually exclusive\n");
433 			}
434 			opts.di_condensed = B_TRUE;
435 			break;
436 		case 'H':
437 			opts.di_scripted = B_TRUE;
438 			break;
439 		case 'P':
440 			if (opts.di_condensed) {
441 				usage(argv[0]);
442 				fatal(1, "-c and -P are mutually exclusive\n");
443 			}
444 			opts.di_physical = B_TRUE;
445 			break;
446 		case 'p':
447 			opts.di_parseable = B_TRUE;
448 			break;
449 		case '?':
450 			usage(argv[0]);
451 			fatal(1, "unknown option -%c\n", optopt);
452 		default:
453 			fatal(-1, "unexpected error on option -%c\n", optopt);
454 		}
455 	}
456 
457 	if (!opts.di_scripted) {
458 		if (opts.di_physical) {
459 			printf("DISK                    VID      PID"
460 			    "              SERIAL               FLT LOC"
461 			    " LOCATION\n");
462 		} else if (opts.di_condensed) {
463 			printf("TYPE    DISK                    VID      PID"
464 			    "              SERIAL\n");
465 			printf("\tSIZE          FLRS LOCATION\n");
466 		} else {
467 			printf("TYPE    DISK                    VID      PID"
468 			    "              SIZE          RMV SSD\n");
469 		}
470 	}
471 
472 	enumerate_disks(&opts);
473 
474 	return (0);
475 }
476