xref: /freebsd/contrib/smart/smart.c (revision 346be36e8861e26bfed44cbf960903d0055f6660)
1 /*
2  * Copyright (c) 2016-2026 Chuck Tuffli <chuck@tuffli.net>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <getopt.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #ifdef LIBXO
23 #include <libxo/xo.h>
24 #endif
25 
26 #include "libsmart.h"
27 
28 #define SMART_NAME "smart"
29 #define SMART_VERSION	"1.0.3"
30 
31 extern bool do_debug;
32 
33 static const char *pn;
34 bool do_debug = false;
35 static int debugset = 0;
36 
37 static struct option opts[] = {
38 	{ "help", no_argument, NULL, 'h' },
39 	{ "threshold", no_argument, NULL, 't' },
40 	{ "hex", no_argument, NULL, 'x' },
41 	{ "attribute", required_argument, NULL, 'a' },
42 	{ "info", no_argument, NULL, 'i' },
43 	{ "version", no_argument, NULL, 'v' },
44 	{ "decode", no_argument, NULL, 'd' },
45 	{ "no-decode", no_argument, NULL, 'D' },
46 	{ "debug", no_argument, &debugset, 1 },
47 	{ NULL, 0, NULL, 0 }
48 };
49 
50 static void
usage(const char * name)51 usage(const char *name)
52 {
53 	printf("Usage: %s [-htxi] [-a attribute[,attribute]...] <device name>\n", name);
54 	printf("\t-h, --help\n");
55 	printf("\t-t, --threshold : also print out the threshold values\n");
56 	printf("\t-x, --hex : print the values out in hexadecimal\n");
57 	printf("\t-a, --attribute : print specified attribute(s)\n");
58 	printf("\t-i, --info : print general device information\n");
59 	printf("\t-d, --decode: decode the attribute IDs\n");
60 	printf("\t-D, --no-decode: don't decode the attribute IDs\n");
61 	printf("\t-v, --version : print the version and copyright\n");
62 	printf("\t    --debug : output diagnostic information\n");
63 }
64 
65 /*
66  * Convert string to an integer
67  *
68  * Returns -1 on error, converted value otherwise
69  */
70 static int32_t
get_val(char * attr,char ** next)71 get_val(char *attr, char **next)
72 {
73 	char *sep = NULL;
74 	long val;
75 
76 	*next = NULL;
77 
78 	val = strtol(attr, &sep, 0);
79 	if ((val == 0) && (errno != 0)) {
80 		printf("Error parsing attribute %s", attr);
81 		switch (errno) {
82 		case EINVAL:
83 			printf(" (not a number?)\n");
84 			break;
85 		case ERANGE:
86 			printf(" (value out of range)\n");
87 			break;
88 		default:
89 			printf("\n");
90 		}
91 		return -1;
92 	}
93 
94 	if (val > INT32_MAX) {
95 		printf("Attribute value %ld too big\n", val);
96 		return -1;
97 	}
98 
99 	*next = sep;
100 	return ((int32_t)val);
101 }
102 
103 /*
104  * Create a match specification from the given attribute
105  *
106  * Attribute format is
107  *     <Page ID>:<Attribute ID>
108  * where page and attribute IDs are integers. If the page ID is missing,
109  * match the specified attribute ID on any page (i.e. -1). Valid forms are
110  *    <int>:<int>
111  *    :<int>
112  *    <int>
113  *
114  * Returns 0 on success
115  */
116 static int
add_match(smart_matches_t ** matches,char * attr)117 add_match(smart_matches_t **matches, char *attr)
118 {
119 	char *next;
120 	int32_t page = -1, id;
121 	uint32_t count = 0;
122 
123 	id = get_val(attr, &next);
124 	if (id < 0)
125 		return id;
126 
127 	if (*next == ':') {
128 		page = id;
129 		id = get_val(next + 1, &next);
130 		if (id < 0)
131 			return id;
132 	}
133 
134 	if (*matches == NULL) {
135 		*matches = calloc(1, sizeof(smart_matches_t) + sizeof(smart_match_t));
136 		if (*matches == NULL)
137 			return ENOMEM;
138 	} else {
139 		void *tmp;
140 
141 		count = (*matches)->count;
142 		tmp = realloc(*matches, sizeof(smart_matches_t) + ((count + 1) * sizeof(smart_match_t)));
143 		if (tmp == NULL)
144 			return ENOMEM;
145 		*matches = tmp;
146 	}
147 
148 	(*matches)->m[count].page = page;
149 	(*matches)->m[count].id = id;
150 	(*matches)->count++;
151 	return 0;
152 }
153 
154 /*
155  * Parse the comma separated list of attributes to match
156  *
157  * Caller frees memory allocated for the smart_matches_t pointer.
158  *
159  * Returns 0 on success
160  */
161 static int
parse_matches(smart_matches_t ** matches,char * attr)162 parse_matches(smart_matches_t **matches, char *attr)
163 {
164 	int res;
165 
166 	if (attr[0] == '\0')
167 		return -1;
168 
169 	while (*attr != '\0') {
170 		char *next;
171 		size_t len;
172 
173 		if ((next = strchr(attr, ',')) == NULL) {
174 			len = strlen(attr);
175 			next = attr + len;
176 		} else {
177 			len = next - attr;
178 			next++;
179 		}
180 
181 		if (len == 0) {
182 			printf("Malformed attribute %s\n", attr);
183 			return -1;
184 		}
185 
186 		res = add_match(matches, attr);
187 		if (res)
188 			return res;
189 
190 		attr = next;
191 	}
192 
193 	return 0;
194 }
195 
196 int
main(int argc,char * argv[])197 main(int argc, char *argv[])
198 {
199 	smart_h h;
200 	smart_map_t *sm = NULL;
201 	char *devname = NULL;
202 	int ch;
203 	bool do_thresh = false, do_hex = false, do_info = false, do_version = false,
204 	     do_descr;
205 	smart_matches_t *matches = NULL;
206 	int rc = EXIT_SUCCESS;
207 
208 	/*
209 	 * By default, keep the original behavior (output numbers only) if
210 	 * invoked as smart. Otherwise, default to printing the human-friendly
211 	 * text descriptions.
212 	 */
213 	pn = getprogname();
214 	if (strcmp(pn, SMART_NAME) == 0)
215 		do_descr = false;
216 	else
217 		do_descr = true;
218 
219 #ifdef LIBXO
220 	argc = xo_parse_args(argc, argv);
221 #endif
222 
223 	while ((ch = getopt_long(argc, argv, "htxa:idDv", opts, NULL)) != -1) {
224 		switch (ch) {
225 		case 'h':
226 			usage(pn);
227 #ifdef LIBXO
228 			xo_finish();
229 #endif
230 			return EXIT_SUCCESS;
231 			break;
232 		case 't':
233 			do_thresh = true;
234 			break;
235 		case 'x':
236 			do_hex = true;
237 			break;
238 		case 'a':
239 			if (parse_matches(&matches, optarg)) {
240 				usage(pn);
241 				return EXIT_FAILURE;
242 			}
243 			break;
244 		case 'i':
245 			do_info = true;
246 			break;
247 		case 'd':
248 			do_descr = true;
249 			break;
250 		case 'D':
251 			do_descr = false;
252 			break;
253 		case 'v':
254 			do_version = true;
255 			break;
256 		case 0:
257 			if (debugset)
258 				do_debug = true;
259 			break;
260 		default:
261 			usage(pn);
262 #ifdef LIBXO
263 			xo_finish();
264 #endif
265 			return EXIT_FAILURE;
266 		}
267 	}
268 
269 	if (do_version) {
270 		printf("%s, version %s\n", pn, SMART_VERSION);
271 		printf("Copyright (c) 2016-2026 Chuck Tuffli\n"
272 				"This is free software; see the source for copying conditions.\n");
273 		return EXIT_SUCCESS;
274 	}
275 
276 	argc -= optind;
277 	argv += optind;
278 
279 	devname = argv[0];
280 
281 	if (!devname) {
282 		printf("no device specified\n");
283 		usage(pn);
284 #ifdef LIBXO
285 		xo_finish();
286 #endif
287 		return EXIT_FAILURE;
288 	}
289 
290 	h = smart_open(SMART_PROTO_AUTO, argv[0]);
291 
292 	if (h == NULL) {
293 		printf("device open failed %s\n", argv[0]);
294 #ifdef LIBXO
295 		xo_finish();
296 #endif
297 		return EXIT_FAILURE;
298 	}
299 
300 #ifdef LIBXO
301 	xo_open_container("drive");
302 #endif
303 
304 	if (do_info) {
305 		smart_print_device_info(h);
306 	}
307 
308 	if (smart_supported(h)) {
309 		sm = smart_read(h);
310 
311 		if (sm) {
312 			uint32_t flags = 0;
313 
314 			if (do_hex)
315 				flags |= SMART_OPEN_F_HEX;
316 			if (do_thresh)
317 				flags |= SMART_OPEN_F_THRESH;
318 			if (do_descr)
319 				flags |= SMART_OPEN_F_DESCR;
320 
321 			smart_print(h, sm, matches, flags);
322 
323 			smart_free(sm);
324 		}
325 	} else {
326 		rc = EXIT_FAILURE;
327 	}
328 #ifdef LIBXO
329 	xo_finish();
330 #endif
331 	smart_close(h);
332 
333 	return rc;
334 }
335