xref: /freebsd/contrib/smart/smart.c (revision 346be36e8861e26bfed44cbf960903d0055f6660)
17419d6e4SChuck Tuffli /*
27419d6e4SChuck Tuffli  * Copyright (c) 2016-2026 Chuck Tuffli <chuck@tuffli.net>
37419d6e4SChuck Tuffli  *
47419d6e4SChuck Tuffli  * Permission to use, copy, modify, and distribute this software for any
57419d6e4SChuck Tuffli  * purpose with or without fee is hereby granted, provided that the above
67419d6e4SChuck Tuffli  * copyright notice and this permission notice appear in all copies.
77419d6e4SChuck Tuffli  *
87419d6e4SChuck Tuffli  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
97419d6e4SChuck Tuffli  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
107419d6e4SChuck Tuffli  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
117419d6e4SChuck Tuffli  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
127419d6e4SChuck Tuffli  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
137419d6e4SChuck Tuffli  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
147419d6e4SChuck Tuffli  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
157419d6e4SChuck Tuffli  */
167419d6e4SChuck Tuffli 
177419d6e4SChuck Tuffli #include <stdio.h>
187419d6e4SChuck Tuffli #include <stdlib.h>
197419d6e4SChuck Tuffli #include <getopt.h>
207419d6e4SChuck Tuffli #include <stdbool.h>
217419d6e4SChuck Tuffli #include <string.h>
227419d6e4SChuck Tuffli #ifdef LIBXO
237419d6e4SChuck Tuffli #include <libxo/xo.h>
247419d6e4SChuck Tuffli #endif
257419d6e4SChuck Tuffli 
267419d6e4SChuck Tuffli #include "libsmart.h"
277419d6e4SChuck Tuffli 
287419d6e4SChuck Tuffli #define SMART_NAME "smart"
29*346be36eSChuck Tuffli #define SMART_VERSION	"1.0.3"
307419d6e4SChuck Tuffli 
317419d6e4SChuck Tuffli extern bool do_debug;
327419d6e4SChuck Tuffli 
337419d6e4SChuck Tuffli static const char *pn;
347419d6e4SChuck Tuffli bool do_debug = false;
357419d6e4SChuck Tuffli static int debugset = 0;
367419d6e4SChuck Tuffli 
377419d6e4SChuck Tuffli static struct option opts[] = {
387419d6e4SChuck Tuffli 	{ "help", no_argument, NULL, 'h' },
397419d6e4SChuck Tuffli 	{ "threshold", no_argument, NULL, 't' },
407419d6e4SChuck Tuffli 	{ "hex", no_argument, NULL, 'x' },
417419d6e4SChuck Tuffli 	{ "attribute", required_argument, NULL, 'a' },
427419d6e4SChuck Tuffli 	{ "info", no_argument, NULL, 'i' },
437419d6e4SChuck Tuffli 	{ "version", no_argument, NULL, 'v' },
447419d6e4SChuck Tuffli 	{ "decode", no_argument, NULL, 'd' },
457419d6e4SChuck Tuffli 	{ "no-decode", no_argument, NULL, 'D' },
467419d6e4SChuck Tuffli 	{ "debug", no_argument, &debugset, 1 },
477419d6e4SChuck Tuffli 	{ NULL, 0, NULL, 0 }
487419d6e4SChuck Tuffli };
497419d6e4SChuck Tuffli 
507419d6e4SChuck Tuffli static void
usage(const char * name)517419d6e4SChuck Tuffli usage(const char *name)
527419d6e4SChuck Tuffli {
537419d6e4SChuck Tuffli 	printf("Usage: %s [-htxi] [-a attribute[,attribute]...] <device name>\n", name);
547419d6e4SChuck Tuffli 	printf("\t-h, --help\n");
557419d6e4SChuck Tuffli 	printf("\t-t, --threshold : also print out the threshold values\n");
567419d6e4SChuck Tuffli 	printf("\t-x, --hex : print the values out in hexadecimal\n");
577419d6e4SChuck Tuffli 	printf("\t-a, --attribute : print specified attribute(s)\n");
587419d6e4SChuck Tuffli 	printf("\t-i, --info : print general device information\n");
597419d6e4SChuck Tuffli 	printf("\t-d, --decode: decode the attribute IDs\n");
607419d6e4SChuck Tuffli 	printf("\t-D, --no-decode: don't decode the attribute IDs\n");
617419d6e4SChuck Tuffli 	printf("\t-v, --version : print the version and copyright\n");
627419d6e4SChuck Tuffli 	printf("\t    --debug : output diagnostic information\n");
637419d6e4SChuck Tuffli }
647419d6e4SChuck Tuffli 
657419d6e4SChuck Tuffli /*
667419d6e4SChuck Tuffli  * Convert string to an integer
677419d6e4SChuck Tuffli  *
687419d6e4SChuck Tuffli  * Returns -1 on error, converted value otherwise
697419d6e4SChuck Tuffli  */
707419d6e4SChuck Tuffli static int32_t
get_val(char * attr,char ** next)717419d6e4SChuck Tuffli get_val(char *attr, char **next)
727419d6e4SChuck Tuffli {
737419d6e4SChuck Tuffli 	char *sep = NULL;
747419d6e4SChuck Tuffli 	long val;
757419d6e4SChuck Tuffli 
767419d6e4SChuck Tuffli 	*next = NULL;
777419d6e4SChuck Tuffli 
787419d6e4SChuck Tuffli 	val = strtol(attr, &sep, 0);
797419d6e4SChuck Tuffli 	if ((val == 0) && (errno != 0)) {
807419d6e4SChuck Tuffli 		printf("Error parsing attribute %s", attr);
817419d6e4SChuck Tuffli 		switch (errno) {
827419d6e4SChuck Tuffli 		case EINVAL:
837419d6e4SChuck Tuffli 			printf(" (not a number?)\n");
847419d6e4SChuck Tuffli 			break;
857419d6e4SChuck Tuffli 		case ERANGE:
867419d6e4SChuck Tuffli 			printf(" (value out of range)\n");
877419d6e4SChuck Tuffli 			break;
887419d6e4SChuck Tuffli 		default:
897419d6e4SChuck Tuffli 			printf("\n");
907419d6e4SChuck Tuffli 		}
917419d6e4SChuck Tuffli 		return -1;
927419d6e4SChuck Tuffli 	}
937419d6e4SChuck Tuffli 
947419d6e4SChuck Tuffli 	if (val > INT32_MAX) {
957419d6e4SChuck Tuffli 		printf("Attribute value %ld too big\n", val);
967419d6e4SChuck Tuffli 		return -1;
977419d6e4SChuck Tuffli 	}
987419d6e4SChuck Tuffli 
997419d6e4SChuck Tuffli 	*next = sep;
1007419d6e4SChuck Tuffli 	return ((int32_t)val);
1017419d6e4SChuck Tuffli }
1027419d6e4SChuck Tuffli 
1037419d6e4SChuck Tuffli /*
1047419d6e4SChuck Tuffli  * Create a match specification from the given attribute
1057419d6e4SChuck Tuffli  *
1067419d6e4SChuck Tuffli  * Attribute format is
1077419d6e4SChuck Tuffli  *     <Page ID>:<Attribute ID>
1087419d6e4SChuck Tuffli  * where page and attribute IDs are integers. If the page ID is missing,
1097419d6e4SChuck Tuffli  * match the specified attribute ID on any page (i.e. -1). Valid forms are
1107419d6e4SChuck Tuffli  *    <int>:<int>
1117419d6e4SChuck Tuffli  *    :<int>
1127419d6e4SChuck Tuffli  *    <int>
1137419d6e4SChuck Tuffli  *
1147419d6e4SChuck Tuffli  * Returns 0 on success
1157419d6e4SChuck Tuffli  */
1167419d6e4SChuck Tuffli static int
add_match(smart_matches_t ** matches,char * attr)1177419d6e4SChuck Tuffli add_match(smart_matches_t **matches, char *attr)
1187419d6e4SChuck Tuffli {
1197419d6e4SChuck Tuffli 	char *next;
1207419d6e4SChuck Tuffli 	int32_t page = -1, id;
1217419d6e4SChuck Tuffli 	uint32_t count = 0;
1227419d6e4SChuck Tuffli 
1237419d6e4SChuck Tuffli 	id = get_val(attr, &next);
1247419d6e4SChuck Tuffli 	if (id < 0)
1257419d6e4SChuck Tuffli 		return id;
1267419d6e4SChuck Tuffli 
1277419d6e4SChuck Tuffli 	if (*next == ':') {
1287419d6e4SChuck Tuffli 		page = id;
1297419d6e4SChuck Tuffli 		id = get_val(next + 1, &next);
1307419d6e4SChuck Tuffli 		if (id < 0)
1317419d6e4SChuck Tuffli 			return id;
1327419d6e4SChuck Tuffli 	}
1337419d6e4SChuck Tuffli 
1347419d6e4SChuck Tuffli 	if (*matches == NULL) {
1357419d6e4SChuck Tuffli 		*matches = calloc(1, sizeof(smart_matches_t) + sizeof(smart_match_t));
1367419d6e4SChuck Tuffli 		if (*matches == NULL)
1377419d6e4SChuck Tuffli 			return ENOMEM;
1387419d6e4SChuck Tuffli 	} else {
1397419d6e4SChuck Tuffli 		void *tmp;
1407419d6e4SChuck Tuffli 
1417419d6e4SChuck Tuffli 		count = (*matches)->count;
1427419d6e4SChuck Tuffli 		tmp = realloc(*matches, sizeof(smart_matches_t) + ((count + 1) * sizeof(smart_match_t)));
1437419d6e4SChuck Tuffli 		if (tmp == NULL)
1447419d6e4SChuck Tuffli 			return ENOMEM;
1457419d6e4SChuck Tuffli 		*matches = tmp;
1467419d6e4SChuck Tuffli 	}
1477419d6e4SChuck Tuffli 
1487419d6e4SChuck Tuffli 	(*matches)->m[count].page = page;
1497419d6e4SChuck Tuffli 	(*matches)->m[count].id = id;
1507419d6e4SChuck Tuffli 	(*matches)->count++;
1517419d6e4SChuck Tuffli 	return 0;
1527419d6e4SChuck Tuffli }
1537419d6e4SChuck Tuffli 
1547419d6e4SChuck Tuffli /*
1557419d6e4SChuck Tuffli  * Parse the comma separated list of attributes to match
1567419d6e4SChuck Tuffli  *
1577419d6e4SChuck Tuffli  * Caller frees memory allocated for the smart_matches_t pointer.
1587419d6e4SChuck Tuffli  *
1597419d6e4SChuck Tuffli  * Returns 0 on success
1607419d6e4SChuck Tuffli  */
1617419d6e4SChuck Tuffli static int
parse_matches(smart_matches_t ** matches,char * attr)1627419d6e4SChuck Tuffli parse_matches(smart_matches_t **matches, char *attr)
1637419d6e4SChuck Tuffli {
1647419d6e4SChuck Tuffli 	int res;
1657419d6e4SChuck Tuffli 
1667419d6e4SChuck Tuffli 	if (attr[0] == '\0')
1677419d6e4SChuck Tuffli 		return -1;
1687419d6e4SChuck Tuffli 
1697419d6e4SChuck Tuffli 	while (*attr != '\0') {
1707419d6e4SChuck Tuffli 		char *next;
1717419d6e4SChuck Tuffli 		size_t len;
1727419d6e4SChuck Tuffli 
1737419d6e4SChuck Tuffli 		if ((next = strchr(attr, ',')) == NULL) {
1747419d6e4SChuck Tuffli 			len = strlen(attr);
1757419d6e4SChuck Tuffli 			next = attr + len;
1767419d6e4SChuck Tuffli 		} else {
1777419d6e4SChuck Tuffli 			len = next - attr;
1787419d6e4SChuck Tuffli 			next++;
1797419d6e4SChuck Tuffli 		}
1807419d6e4SChuck Tuffli 
1817419d6e4SChuck Tuffli 		if (len == 0) {
1827419d6e4SChuck Tuffli 			printf("Malformed attribute %s\n", attr);
1837419d6e4SChuck Tuffli 			return -1;
1847419d6e4SChuck Tuffli 		}
1857419d6e4SChuck Tuffli 
1867419d6e4SChuck Tuffli 		res = add_match(matches, attr);
1877419d6e4SChuck Tuffli 		if (res)
1887419d6e4SChuck Tuffli 			return res;
1897419d6e4SChuck Tuffli 
1907419d6e4SChuck Tuffli 		attr = next;
1917419d6e4SChuck Tuffli 	}
1927419d6e4SChuck Tuffli 
1937419d6e4SChuck Tuffli 	return 0;
1947419d6e4SChuck Tuffli }
1957419d6e4SChuck Tuffli 
1967419d6e4SChuck Tuffli int
main(int argc,char * argv[])1977419d6e4SChuck Tuffli main(int argc, char *argv[])
1987419d6e4SChuck Tuffli {
1997419d6e4SChuck Tuffli 	smart_h h;
2007419d6e4SChuck Tuffli 	smart_map_t *sm = NULL;
2017419d6e4SChuck Tuffli 	char *devname = NULL;
2027419d6e4SChuck Tuffli 	int ch;
2037419d6e4SChuck Tuffli 	bool do_thresh = false, do_hex = false, do_info = false, do_version = false,
2047419d6e4SChuck Tuffli 	     do_descr;
2057419d6e4SChuck Tuffli 	smart_matches_t *matches = NULL;
2067419d6e4SChuck Tuffli 	int rc = EXIT_SUCCESS;
2077419d6e4SChuck Tuffli 
2087419d6e4SChuck Tuffli 	/*
2097419d6e4SChuck Tuffli 	 * By default, keep the original behavior (output numbers only) if
2107419d6e4SChuck Tuffli 	 * invoked as smart. Otherwise, default to printing the human-friendly
2117419d6e4SChuck Tuffli 	 * text descriptions.
2127419d6e4SChuck Tuffli 	 */
2137419d6e4SChuck Tuffli 	pn = getprogname();
2147419d6e4SChuck Tuffli 	if (strcmp(pn, SMART_NAME) == 0)
2157419d6e4SChuck Tuffli 		do_descr = false;
2167419d6e4SChuck Tuffli 	else
2177419d6e4SChuck Tuffli 		do_descr = true;
2187419d6e4SChuck Tuffli 
2197419d6e4SChuck Tuffli #ifdef LIBXO
2207419d6e4SChuck Tuffli 	argc = xo_parse_args(argc, argv);
2217419d6e4SChuck Tuffli #endif
2227419d6e4SChuck Tuffli 
2237419d6e4SChuck Tuffli 	while ((ch = getopt_long(argc, argv, "htxa:idDv", opts, NULL)) != -1) {
2247419d6e4SChuck Tuffli 		switch (ch) {
2257419d6e4SChuck Tuffli 		case 'h':
2267419d6e4SChuck Tuffli 			usage(pn);
2277419d6e4SChuck Tuffli #ifdef LIBXO
2287419d6e4SChuck Tuffli 			xo_finish();
2297419d6e4SChuck Tuffli #endif
2307419d6e4SChuck Tuffli 			return EXIT_SUCCESS;
2317419d6e4SChuck Tuffli 			break;
2327419d6e4SChuck Tuffli 		case 't':
2337419d6e4SChuck Tuffli 			do_thresh = true;
2347419d6e4SChuck Tuffli 			break;
2357419d6e4SChuck Tuffli 		case 'x':
2367419d6e4SChuck Tuffli 			do_hex = true;
2377419d6e4SChuck Tuffli 			break;
2387419d6e4SChuck Tuffli 		case 'a':
2397419d6e4SChuck Tuffli 			if (parse_matches(&matches, optarg)) {
2407419d6e4SChuck Tuffli 				usage(pn);
2417419d6e4SChuck Tuffli 				return EXIT_FAILURE;
2427419d6e4SChuck Tuffli 			}
2437419d6e4SChuck Tuffli 			break;
2447419d6e4SChuck Tuffli 		case 'i':
2457419d6e4SChuck Tuffli 			do_info = true;
2467419d6e4SChuck Tuffli 			break;
2477419d6e4SChuck Tuffli 		case 'd':
2487419d6e4SChuck Tuffli 			do_descr = true;
2497419d6e4SChuck Tuffli 			break;
2507419d6e4SChuck Tuffli 		case 'D':
2517419d6e4SChuck Tuffli 			do_descr = false;
2527419d6e4SChuck Tuffli 			break;
2537419d6e4SChuck Tuffli 		case 'v':
2547419d6e4SChuck Tuffli 			do_version = true;
2557419d6e4SChuck Tuffli 			break;
2567419d6e4SChuck Tuffli 		case 0:
2577419d6e4SChuck Tuffli 			if (debugset)
2587419d6e4SChuck Tuffli 				do_debug = true;
2597419d6e4SChuck Tuffli 			break;
2607419d6e4SChuck Tuffli 		default:
2617419d6e4SChuck Tuffli 			usage(pn);
2627419d6e4SChuck Tuffli #ifdef LIBXO
2637419d6e4SChuck Tuffli 			xo_finish();
2647419d6e4SChuck Tuffli #endif
2657419d6e4SChuck Tuffli 			return EXIT_FAILURE;
2667419d6e4SChuck Tuffli 		}
2677419d6e4SChuck Tuffli 	}
2687419d6e4SChuck Tuffli 
2697419d6e4SChuck Tuffli 	if (do_version) {
2707419d6e4SChuck Tuffli 		printf("%s, version %s\n", pn, SMART_VERSION);
2717419d6e4SChuck Tuffli 		printf("Copyright (c) 2016-2026 Chuck Tuffli\n"
2727419d6e4SChuck Tuffli 				"This is free software; see the source for copying conditions.\n");
2737419d6e4SChuck Tuffli 		return EXIT_SUCCESS;
2747419d6e4SChuck Tuffli 	}
2757419d6e4SChuck Tuffli 
2767419d6e4SChuck Tuffli 	argc -= optind;
2777419d6e4SChuck Tuffli 	argv += optind;
2787419d6e4SChuck Tuffli 
2797419d6e4SChuck Tuffli 	devname = argv[0];
2807419d6e4SChuck Tuffli 
2817419d6e4SChuck Tuffli 	if (!devname) {
2827419d6e4SChuck Tuffli 		printf("no device specified\n");
2837419d6e4SChuck Tuffli 		usage(pn);
2847419d6e4SChuck Tuffli #ifdef LIBXO
2857419d6e4SChuck Tuffli 		xo_finish();
2867419d6e4SChuck Tuffli #endif
2877419d6e4SChuck Tuffli 		return EXIT_FAILURE;
2887419d6e4SChuck Tuffli 	}
2897419d6e4SChuck Tuffli 
2907419d6e4SChuck Tuffli 	h = smart_open(SMART_PROTO_AUTO, argv[0]);
2917419d6e4SChuck Tuffli 
2927419d6e4SChuck Tuffli 	if (h == NULL) {
2937419d6e4SChuck Tuffli 		printf("device open failed %s\n", argv[0]);
2947419d6e4SChuck Tuffli #ifdef LIBXO
2957419d6e4SChuck Tuffli 		xo_finish();
2967419d6e4SChuck Tuffli #endif
2977419d6e4SChuck Tuffli 		return EXIT_FAILURE;
2987419d6e4SChuck Tuffli 	}
2997419d6e4SChuck Tuffli 
3007419d6e4SChuck Tuffli #ifdef LIBXO
3017419d6e4SChuck Tuffli 	xo_open_container("drive");
3027419d6e4SChuck Tuffli #endif
3037419d6e4SChuck Tuffli 
3047419d6e4SChuck Tuffli 	if (do_info) {
3057419d6e4SChuck Tuffli 		smart_print_device_info(h);
3067419d6e4SChuck Tuffli 	}
3077419d6e4SChuck Tuffli 
3087419d6e4SChuck Tuffli 	if (smart_supported(h)) {
3097419d6e4SChuck Tuffli 		sm = smart_read(h);
3107419d6e4SChuck Tuffli 
3117419d6e4SChuck Tuffli 		if (sm) {
3127419d6e4SChuck Tuffli 			uint32_t flags = 0;
3137419d6e4SChuck Tuffli 
3147419d6e4SChuck Tuffli 			if (do_hex)
3157419d6e4SChuck Tuffli 				flags |= SMART_OPEN_F_HEX;
3167419d6e4SChuck Tuffli 			if (do_thresh)
3177419d6e4SChuck Tuffli 				flags |= SMART_OPEN_F_THRESH;
3187419d6e4SChuck Tuffli 			if (do_descr)
3197419d6e4SChuck Tuffli 				flags |= SMART_OPEN_F_DESCR;
3207419d6e4SChuck Tuffli 
3217419d6e4SChuck Tuffli 			smart_print(h, sm, matches, flags);
3227419d6e4SChuck Tuffli 
3237419d6e4SChuck Tuffli 			smart_free(sm);
3247419d6e4SChuck Tuffli 		}
3257419d6e4SChuck Tuffli 	} else {
3267419d6e4SChuck Tuffli 		rc = EXIT_FAILURE;
3277419d6e4SChuck Tuffli 	}
3287419d6e4SChuck Tuffli #ifdef LIBXO
3297419d6e4SChuck Tuffli 	xo_finish();
3307419d6e4SChuck Tuffli #endif
3317419d6e4SChuck Tuffli 	smart_close(h);
3327419d6e4SChuck Tuffli 
3337419d6e4SChuck Tuffli 	return rc;
3347419d6e4SChuck Tuffli }
335